Normes de codage des tests unitaires

22

Habituellement, lorsque nous parlons de normes de codage, nous nous référons au code du programme lui-même, mais qu'en est-il des tests unitaires? Existe-t-il certaines directives de normes de codage propres aux tests unitaires? Que sont-ils?

EpsilonVector
la source

Réponses:

12

Du haut de ma tête, je peux penser à trois différences de style de codage pour le code de test.

Pour nommer les méthodes de test, je suis le modèle de shouldDoSomethingWhenSomeConditionHolds.

À l'intérieur du test, il est habituel de suivre le modèle d'espacement suivant:

@Test
shouldReturnAccountBalenceWhenGetBalenceIsCalled() {
    // Some lines 
    // of setup code
    // go here.

    // The action being tested happens after a blank line.

    // An assertion follows another blank line.
}

Certains insistent sur une seule assertion par test, mais cela est loin d'être universel.

Le DRY (Don't Repeat Yourself) est moins pris en compte dans le code de test que dans le code de production. Alors que du code répété doit être placé dans une méthode setUp ou une classe testUtils, la recherche d'une répétition nulle dans le code de test conduira à des tests étroitement couplés et inflexibles, ce qui décourage le refactoring.

Eric Wilson
la source
Bien sûr, il existe une variété de modèles, c'est pourquoi vous devez également fournir une réponse.
Eric Wilson
10
C'est le modèle Arrange, Act, Assert .
StuperUser
SEC est toujours important. Si vous devez effectuer les mêmes assertions dans plusieurs tests, créez une fonction commune et appelez-la dans tous les tests.
MiFreidgeim SO-stop étant maléfique
@MichaelFreidgeim Peut-être que nous parlons simplement de degrés, mais j'ai une tolérance beaucoup plus élevée pour la répétition dans le code de test. J'ai eu plusieurs expériences de construction de suites de tests avec très peu de répétitions et j'ai constaté que les tests devenaient difficiles à modifier et à comprendre lorsque les exigences changeaient. Ensuite, j'ai cessé de m'inquiéter autant de DRY dans les tests, et mes suites de tests ont été plus faciles à utiliser. <shrug>
Eric Wilson
16

Roy Osherove recommande le modèle suivant pour nommer vos tests:

NameOfMethodUnderTest_StateUnderTest_ExpectedBehavior() 

Voir http://weblogs.asp.net/rosherove/archive/2005/04/03/TestNamingStandards.aspx

Robert Harvey
la source
Je suis d'accord avec Roy. Cela améliore la lisibilité, bien que ReSharper me dise que je devrais les supprimer NameOfMethodUnderTestStateUnderTestExpectedBehavior();)
Oscar Mederos
Comment faire fonctionner cela lorsque la méthode est surchargée, donc il peut y avoir plusieurs méthodes avec le même nom?
Narendra Pathai
6

L'essentiel est de se rappeler que les tests unitaires sont essentiellement des mini-spécifications. Cela signifie que l'accent doit toujours être mis sur la lisibilité.

Premièrement, cela signifie que les noms doivent clairement communiquer ce qui est testé et ce qui est affirmé.

Deuxièmement, ce qui est parfois oublié, c'est qu'en tant que spécifications, ils devraient faire exactement cela - en spécifiant le comportement. Autrement dit, les tests unitaires ne doivent pas contenir de logique - ou ils risquent de tomber dans le piège de répéter les fonctionnalités du programme plutôt que de les tester.

Parfois, les tests impliquent des objets complexes à configurer, vous devez vous efforcer de garder cette logique de configuration distincte de vos tests en utilisant quelque chose comme une mère d'objet ou un générateur de données de test .

Je vais juste terminer avec quelques recommandations de livres:

xUnit Test Patterns: Refactoring Test Code: Excellent livre, certains disent que c'est un peu sec mais je ne pense pas. Aborde en détail de nombreuses façons d'organiser les tests et de les maintenir maintenables. Pertinent si vous utilisez quelque chose comme NUnit, etc.

L'art des tests unitaires: avec des exemples dans .Net : le meilleur livre sur le vif du sujet de la rédaction et de la maintenance des tests. En dépit d'être vraiment nouveau, je trouve que les sections moqueuses sont déjà un peu datées car la syntaxe AAA est maintenant assez standard plutôt que juste une autre façon de le faire.

Développement d'un logiciel orienté objet, guidé par des tests : ce livre est tout simplement incroyable! De loin le meilleur livre de tests unitaires et le seul avancé qui place les tests unitaires en tant que citoyen de première classe dans le processus de conception. Je lisais cela quand c'était une version bêta publique et je le recommande depuis. Excellent exemple de travail réaliste utilisé dans tout le livre. Je recommanderais cependant de lire le livre de Roy en premier.

FinnNk
la source
À mon humble avis, les tests unitaires contiennent de la logique: il est parfaitement raisonnable de tester une version hautement optimisée et efficace d'un algorithme en utilisant un algorithme naïf qui fait la même chose pour déterminer le comportement correct. Par exemple, imaginez tester une table de hachage en créant un tableau associatif basé sur la recherche linéaire.
dsimcha
2
Oui, mais cela appartient en dehors du test dans les générateurs de données de test (qui devraient eux-mêmes être testés unitaire si la logique à l'intérieur d'eux n'est pas triviale). Une exception à cela serait les bibliothèques tierces qui sont généralement "fiables" pour être correctes et pourraient être utilisées sans tests.
FinnNk
3

Ne mettez pas de logique dans vos tests unitaires. Par exemple, supposons que vous testiez une méthode d'ajout, vous pourriez avoir quelque chose comme ceci:

void MyTest_SaysHello()
{
   string name = "Bob";
   string expected = string.Format("Hello, {0}", name);
   IMyObjectType myObject = new MyObjectType();
   string actual = myObject.SayHello(name);
   Assert.AreEqual(expected, actual);
}

Dans ce cas particulier, vous répétez probablement la même logique que ce qui est dans le test, donc vous testez essentiellement "1 + 1 == 1 + 1", plutôt que "1 + 1 == 2", qui est le "vrai" test. Donc, à quoi vous voudriez vraiment que votre code de test ressemble:

void MyTest_SaysHello()
{
   string expected = "Hello, Bob";
   IMyObjectType myObject = new MyObjectType();
   string actual = myObject.SayHello("Bob");
   Assert.AreEqual(expected, actual);
}
Hugo
la source
2
Petite correction: je pense que vous vouliez dire 'chaîne attendue = chaîne.Format ("Bonjour, Bob")' devrait être 'chaîne attendue = "Bonjour, Bob"'.
Mike Rosenblum
@MikeRosenblum vous avez évidemment raison, et quelqu'un a essayé de le corriger, mais deux critiques ont rejeté cette modification
Konrad Morawski
@ Konrad: c'est étrange. Ceci est un forum de programmation, non?
Mike Rosenblum
J'ai de nouveau édité la réponse comme suggéré par Mike Rosenblum.
bdsl
0

Noms de méthode longs et descriptifs. N'oubliez pas que les méthodes de test ne sont jamais appelées à partir du code (elles sont appelées par le programme de test unitaire qui les découvre et les appelle via la réflexion), il est donc normal de devenir fou et d'avoir des noms de méthode de 50 à 80 caractères. La convention de dénomination spécifique (cas de chameau, traits de soulignement, "devrait", "doit", "quand", "donné", etc.) n'est pas vraiment importante tant que le nom répond à trois questions:

  • qu'est-ce qui est testé?
  • quelles sont les conditions?
  • quel est le résultat attendu?

Les méthodes d'essai doivent être courtes .

Les méthodes d'essai doivent avoir une structure simple et linéaire . Aucune construction if ou loop.

Les méthodes de test doivent suivre le modèle "organiser-agir-affirmer" .

Chaque test doit tester une chose . Cela signifie généralement une assertion par test. Un test comme celui-ci { Do A; Assert B; Assert C; }devrait être refactorisé en deux: { Do A; Assert B; }et{ Do A; Assert C; }

Évitez les données aléatoires ou des choses comme «DateTime.Now»

Assurez-vous que tous les éléments du dispositif de test sont ramenés à leur état d'origine à la fin du test (par exemple à l'aide d'un démontage )

Même si vous supprimez impitoyablement la duplication dans votre code de production, la duplication de code dans les montages de test est une préoccupation beaucoup plus petite.

azheglov
la source
-1

Un peu similaire à ce que Farmboy a déjà mentionné, le format de mon nom de méthode

 <MethodName>Should<actionPerformed>When<Condition>

par exemple

 GetBalanceShouldReturnAccountBalance() {
Amit Wadhwa
la source