Je teste qu'une fonction fait ce que l'on attend d'une liste. Je veux donc tester
f(null) -> null
f(empty) -> empty
f(list with one element) -> list with one element
f(list with 2+ elements) -> list with the same number of elements, doing what expected
Pour ce faire, quelle est la meilleure approche?
- Test de tous les cas dans le même test (méthode), sous le nom "WorksAsExpected"
- Placer un test pour chaque cas, ayant ainsi
- "WorksAsExpectedWhenNull"
- "WorksAsExpectedWhenEmpty"
- "WorksAsExpectedWhenSingleElement"
- "WorksAsExpectedWhenMoreElements"
- Un autre choix auquel je ne pensais pas :-)
unit-testing
tdd
malarres
la source
la source
Réponses:
La règle générale que j'utilise pour effectuer un ensemble de tests dans un ou plusieurs cas de test est la suivante: implique-t-il une seule configuration?
Donc, si je testais que, pour plusieurs éléments, il les a tous traités et a dérivé le résultat correct, je peux avoir deux assertions ou plus, mais je n'ai à configurer la liste qu'une seule fois. Donc, un cas de test est très bien.
Dans votre cas cependant, je devrais mettre en place une liste nulle, une liste vide, etc. C'est plusieurs configurations. Je créerais donc certainement plusieurs tests dans ce cas.
Comme d'autres l'ont mentionné, ces «tests multiples» pourraient exister en tant que scénario de test paramétré unique; c'est-à-dire que le même scénario de test est exécuté avec une variété de données de configuration. La clé pour savoir si cette solution est viable réside dans les autres parties du test: «action» et «affirmer». Si vous pouvez effectuer les mêmes actions et assertions sur chaque ensemble de données, utilisez cette approche. Si vous vous retrouvez à ajouter
if
des par exemple pour exécuter différents codes sur différentes parties de ces données, alors ce n'est pas la solution. Utilisez des cas de test individuels dans ce dernier cas.la source
Il y a un compromis. Plus vous emballerez dans un test, plus vous aurez probablement un effet oignon en essayant de le faire passer. En d'autres termes, le tout premier échec arrête ce test. Vous ne connaîtrez pas les autres assertions tant que vous n'aurez pas corrigé le premier échec. Cela dit, avoir un tas de tests unitaires qui sont généralement similaires, sauf pour le code de configuration, est un travail très chargé juste pour découvrir que certains travaux sont écrits et d'autres non.
Outils possibles, basés sur votre framework:
Assume.that()
saute simplement le test des données s'il échoue à la condition préalable. Cela vous permet de définir "Fonctionne comme prévu", puis d'alimenter simplement un grand nombre de données. Lorsque vous affichez les résultats, vous disposez d'une entrée pour les tests parents, puis d'une sous-entrée pour chaque élément de données.Je ne suis pas de la mentalité stricte qu'il ne peut y avoir qu'une seule
assert
déclaration dans votre test, mais je mets les restrictions que toutes les assertions devraient tester les post-conditions d'une seule action. Si la seule différence entre les tests réside dans les données, je suis d'accord pour utiliser les fonctionnalités de test pilotées par les données plus avancées comme les tests paramétrés ou les théories.Évaluez vos options pour décider du meilleur résultat. Je dirai que "WorksAsExpectedWhenNull" est fondamentalement différent de tous les cas où vous avez affaire à une collection qui a un nombre variable d'éléments.
la source
Ce sont des cas de test différents, mais le code du test est le même. L'utilisation de tests paramétrés est donc la meilleure solution. Si votre infrastructure de test ne prend pas en charge le paramétrage, extrayez le code partagé dans une fonction d'assistance et appelez-le à partir de cas de test individuels.
Essayez d'éviter la paramétrisation via une boucle dans un cas de test, car cela rend difficile la détermination de l'ensemble de données à l'origine de l'erreur.
Dans votre cycle TDD rouge – vert – refactor, vous devez ajouter un exemple d'ensemble de données à la fois. La combinaison de plusieurs cas de test en un test paramétré ferait partie de l'étape de refactorisation.
Une approche assez différente est le test de propriété . Vous devez créer divers tests (paramétrés) qui affirment diverses propriétés de votre fonction, sans spécifier de données d'entrée concrètes. Par exemple, une propriété pourrait être: pour toutes les listes
xs
, la listeys = f(xs)
a la même longueur quexs
. Le cadre de test générerait alors des listes intéressantes et des listes aléatoires, et affirmerait que vos propriétés sont valables pour toutes. Cela s'éloigne de la spécification manuelle d'exemples, car le choix manuel d'exemples pourrait manquer des cas de bord intéressants.la source
Avoir un test pour chaque cas est approprié car tester un seul concept dans chaque test est une bonne ligne directrice qui est souvent recommandée.
Voir cet article: Est-il acceptable d'avoir plusieurs assertions dans un seul test unitaire? . Il y a également une discussion pertinente et détaillée:
[...]
la source
À mon avis, cela dépend de la condition du test.
la source