Adhérer à une affirmation par test de cohérence insensée dans ce cas?

10

J'ai une classe que je teste. La classe a une fonction:apply(List<IRule> rules, List<ITarget> targets);

Dans un test, je veux m'assurer que chaque cible a été passée à une règle, à la:

rule1.AssertWasCalled(fnord => fnord.Test(target1));
rule1.AssertWasCalled(fnord => fnord.Test(target2));
rule1.AssertWasCalled(fnord => fnord.Test(target3));

Il me semble que se limiter à une seule affirmation serait tout à fait le hobgoblin . Ai-je raison dans cette hypothèse, ou y a-t-il une autre façon d'affirmer que chaque cible a en fait été testée?

Wayne Werner
la source
Je peux voir les fnords!
Ross Patterson

Réponses:

15

Les trois assertions sont essentiellement un test. Vous testez le comportement d'une méthode sur une collection, pour vous assurer que chaque élément a été un paramètre pour un appel spécifique (c'est-à-dire que chaque élément a été traité correctement).

La configuration des données trois fois et selon trois méthodes différentes est un gaspillage et moins lisible que l'alternative d'avoir plusieurs assertions.

La "règle" d'assertion unique consiste davantage à effectuer des assertions de différents types dans les mêmes méthodes (essentiellement des tests de choses différentes), ce qui ne s'applique pas vraiment dans ce cas, où vous testez un seul comportement.

Oded
la source
3
En effet: la règle est plus une assertion logique par test unitaire. Vous pouvez les regrouper en un seul niveau plus élevé que vous pouvez réutiliser dans différents tests.
Laurent Bourgault-Roy
5

Je pense que cette affirmation par règle de test existe pour garder vos tests concentrés sur un problème. Si vous testez 20 choses en un seul test, il est vraiment difficile de dire quelle est votre couverture. Vous savez que cela cause un problème lorsque vous ne pouvez pas nommer la méthode de test sans le mot et en lui. Par exemple, si votre méthode de test est nommée avec plus de précision testFooIsTrueAndDbExistsAndBarIsNullAndAnExceptionDoesntOccur(), vous testez probablement trop dans un seul test.

Dans votre cas, je pense qu'il est probablement correct d'affirmer trois fois. Si vous souhaitez rendre votre code plus lisible, vous pouvez extraire ces trois assertions dans une méthode nommée assertWasCalledOnTargets(...).

Daniel Kaplan
la source
3

Pour votre exemple particulier, vous pouvez vous en sortir avec "une" déclaration assert si vous faites quelque chose comme:

foreach target in targets
{
     rule1.AssertWasCalled(fnord => fnord.Test(target))
}

C'est ce que je fais pour éviter de me sentir coupable d'avoir plusieurs assertions en un seul test.

Jeff B
la source
Je l'ai déjà fait auparavant. Pas mal comme chemin. Il est facile à lire et vous pouvez comprendre la nature de son action.
CokoBWare
1

J'ai aussi eu du mal avec celui-ci.

Le puriste (en moi) insiste sur une assertion par test donc je saurai * exactement * où les choses ont explosé.

Et puis je me retrouve à couper / coller beaucoup du même code de configuration de test redondant. Après la troisième ou quatrième couche de cela, vous commencez à dire "Oy! Assez!"

Mon compromis a été de trouver les aspects qui ne "cassent" jamais. Et je vais superposer ces pièces ensemble, puis ajouter un nouvel élément qui pourrait se briser. Juste pour être clair, superposer plusieurs zones volatiles en un seul test serait une violation de ce compromis.


la source
1
Vous devriez vérifier Assume. Je viens de l'apprendre aujourd'hui.
Wayne Werner
1

Si le code de configuration de target1est différent du code de configuration de target2, ce type de coupe d'angle a tendance à conduire à un code d'initialisation de test trop long. Ceci est à son tour soit un gâchis ou finit par être refactorisé et réutilisé. Si vos tests sont suffisamment complexes pour justifier leur refactorisation, votre test teste probablement plus d'une chose.

Si le code de configuration pour chaque cible est essentiellement le même, la division de votre test en plusieurs tests individuels est probablement exagérée.

Si target1et target2sont différentes implémentations de la même interface, vous devez plutôt ajouter un test unitaire à l'interface (et permettre à votre infrastructure de test de générer un test pour chaque implémentation de cette interface).

Brian
la source