J'ai un morceau de code qui ressemble à ceci:
function bool PassesBusinessRules()
{
bool meetsBusinessRules = false;
if (PassesBusinessRule1
&& PassesBusinessRule2
&& PassesBusinessRule3)
{
meetsBusinessRules= true;
}
return meetsBusinessRules;
}
Je pense qu'il devrait y avoir quatre tests unitaires pour cette fonction particulière. Trois pour tester chacune des conditions dans l'instruction if et s'assurer qu'elle retourne false. Et un autre test qui garantit que la fonction retourne vrai.
Question: Devrait-il y avoir en fait dix tests unitaires à la place? Neuf qui vérifie chacun des chemins d'échec possibles. C'EST À DIRE:
Et ainsi de suite pour chaque combinaison possible.
Je pense que c'est exagéré, mais certains des autres membres de mon équipe ne le font pas. La façon dont je le vois est que si BusinessRule1 échoue, il devrait toujours retourner faux, peu importe si elle a été vérifiée en premier ou en dernier.
Officiellement, ces types de couverture ont des noms.
Tout d'abord, il y a couverture des prédicats: vous voulez avoir un scénario de test qui rend l'instruction if vraie et une qui la rend fausse. Le respect de cette couverture est probablement une condition de base pour une bonne suite de tests.
Ensuite, il Couverture de la condition: Ici, vous voulez tester que chaque sous-condition dans le if a la valeur true et false. Cela crée évidemment plus de tests, mais il détecte généralement plus de bugs, donc c'est souvent une bonne idée d'inclure dans votre suite de tests si vous en avez le temps.
Les critères de couverture les plus avancés sont généralement appelés Couverture des conditions combinatoires: Ici, l'objectif est d'avoir un cas de test qui passe par toutes les combinaisons possibles de valeurs booléennes dans votre test.
Est-ce mieux qu'une simple couverture de prédicat ou de condition? En termes de couverture, bien sûr. Mais ce n'est pas gratuit. Il a un coût très élevé pour la maintenance des tests. Pour cette raison, la plupart des personnes ne se soucient pas d'une couverture combinatoire complète. Habituellement, tester toutes les branches (ou toutes les conditions) sera suffisant pour détecter les bogues. L'ajout de tests supplémentaires pour les tests combinatoires n'attrapera généralement pas plus de bugs, mais nécessite beaucoup d'efforts pour créer et maintenir. L'effort supplémentaire fait généralement que cela ne vaut pas le très petit gain, donc je ne recommanderais pas cela.
Une partie de cette décision devrait être basée sur le degré de risque que vous pensez que ce code sera. S'il a beaucoup de place pour échouer, cela vaut la peine d'être testé. S'il est quelque peu stable et ne changera pas grand-chose, vous devriez envisager de concentrer vos efforts de test ailleurs.
En fin de compte, cela dépend de vous (équipe r), du code et de l'environnement spécifique du projet. Il n'y a pas de règle universelle. Vous (équipe r) devez écrire autant de tests que nécessaire pour vous assurer que le code est bien correct. Donc, si vos coéquipiers ne sont pas convaincus par 4 tests, vous en aurez peut-être besoin de plus.
OTOH temps pour écrire des tests unitaires est généralement une ressource rare. Alors efforcez-vous de trouver la meilleure façon de passer le temps limité dont vous disposez. Par exemple. si vous avez une autre méthode importante avec une couverture de 0%, il peut être préférable d'écrire quelques tests unitaires pour couvrir celle-ci, plutôt que d'ajouter des tests supplémentaires pour cette méthode. Bien sûr, cela dépend aussi de la fragilité de la mise en œuvre de chacun. La planification de nombreux changements à cette méthode particulière dans un avenir prévisible peut justifier une couverture supplémentaire des tests unitaires. Il en va de même pour le chemin critique à l'intérieur du programme. Ce sont tous des facteurs que vous seul (l'équipe r) pouvez évaluer.
Personnellement, je serais généralement satisfait des 4 tests que vous décrivez, à savoir:
plus peut-être un:
pour garantir que le seul moyen d'obtenir une valeur de retour de true
est de satisfaire aux 3 règles métier. Mais au final, si vos coéquipiers insistent pour que les chemins combinatoires soient couverts, il peut être moins cher d'ajouter ces tests supplémentaires que de continuer l'argument beaucoup plus longtemps :-)
Si vous voulez être sûr, vous aurez besoin de huit tests unitaires en utilisant les conditions représentées par une table de vérité à trois variables ( http://teach.valdosta.edu/plmoch/MATH4161/Spring%202004/and_or_if_files/image006). gif ).
Vous ne pouvez jamais être sûr que la logique métier stipulera toujours que les vérifications sont effectuées dans cet ordre et vous voulez que le test connaisse le moins possible la mise en œuvre réelle.
Oui, il devrait y avoir la combinaison complète dans un monde idéal.
Lorsque vous effectuez le test unitaire, vous devez vraiment essayer d'ignorer comment la méthode fait son travail. Fournissez simplement les 3 entrées et vérifiez que la sortie est correcte.
L'État est mauvais. La fonction suivante n'a pas besoin d'un test unitaire car elle n'a pas d'effets secondaires et il est bien compris ce qu'elle fait et ce qu'elle ne fait pas. Pourquoi le tester? Ne fais-tu pas confiance à ton propre cerveau ??? Les fonctions statiques sont super!
static function bool Foo(bool a, bool b, bool c)
{
return a && b && c;
}
Je sais que cette question est assez ancienne. Mais je veux donner une autre perspective au problème.
Tout d'abord, vos tests unitaires devraient avoir deux objectifs:
what's the class' intention
et how the class is doing its work
Donc, en récapitulant le problème, nous voulons tester un complex if statement
, pour l'exemple donné, il y a 2 ^ 3 possibilités, c'est une quantité importante de tests que nous pouvons écrire.
what is doing the code
En revanche, si vous êtes dans la position que vos tests sont encore plus complexes que l'implémentation, c'est parce que l'implémentation doit être repensée (plus ou moins selon les cas) plutôt que le test lui-même.
Pour les instructions if complexes, par exemple, vous pourriez penser à modèle de responsabilité de chaîne , implémentant chaque gestionnaire de cette façon:
If some simple business rule apply, derive to the next handler
Dans quelle mesure serait-il simple de tester diverses règles simples au lieu d'une règle complexe?
J'espère que cela aide,
C'est l'un de ces cas où quelque chose comme quickcheck ( http://en.wikipedia.org/wiki/QuickCheck ) sera votre ami. Au lieu d'écrire tous les N cas à la main, l'ordinateur doit générer tous (ou au moins un grand nombre) de cas de test possibles et valider que tous renvoient un résultat sensible.
Nous programmons des ordinateurs pour vivre ici, pourquoi ne pas programmer l'ordinateur pour générer vos cas de test pour vous?
Vous pouvez transformer les conditions en conditions de garde:
if (! PassesBusinessRule1) {
return false;
}
if (! PassesBusinessRule2) {
return false;
}
if (! PassesBusinessRule3) {
return false;
}
Je ne pense pas que cela réduit le nombre de cas, mais mon expérience est qu'il est plus facile de les répartir de cette façon.
(Notez que je suis un grand fan de "point de sortie unique", mais je fais une exception pour les conditions de garde. Mais il existe d'autres façons de structurer le code afin que vous n'ayez pas de retours séparés.)