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?
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?
Réponses:
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.
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
NameOfMethodUnderTestStateUnderTestExpectedBehavior()
;)
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.
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);
}
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:
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.
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() {