Les tests unitaires à assertion unique ne violent-ils pas le principe DRY?


12

Chaque fois que j'écris des tests unitaires, j'ai toujours essayé d'avoir une seule assertion par test pour faciliter le débogage lorsque les tests échouent. Cependant, en suivant cette règle, j'ai l'impression de copier constamment le même code dans chaque test et en ayant plus de tests, il devient plus difficile de revenir en arrière pour lire et maintenir.

Les tests à assertion simple violent-ils donc DRY?

Et y a-t-il une bonne règle à suivre pour trouver un bon équilibre, comme avoir un seul test par méthode ? *

* Je me rends compte qu'il n'y a probablement pas de solution universelle à cela, mais existe-t-il une façon recommandée d'aborder cela?


5
Vous pouvez extraire le code que vous copiez dans des méthodes
Ismail Badawi

@IsmailBadawi, cela semble être une bonne idée. Je suppose que ces méthodes devraient renvoyer une instance de l'objet de classe que je teste
Korey Hinton

1
Vous créez quelque chose dans un état donné à tester? Cela ressemble à un montage.
Dave Hillier

@DaveHillier oui, j'ai appris un nouveau mot aujourd'hui. Merci :)
Korey Hinton

dépend de la façon dont vous interprétez 1 assertion par test, si vous voulez dire un appel Assert * alors oui si vous voulez également vous assurer que les invariants tiennent toujours (puis extrayez à nouveau cela dans une méthode), ou s'il y a plusieurs effets que vous pouvez simplement ' test dans un seul Assert (ou si vous l'avez fait, il ne serait pas clair pourquoi il a échoué)
ratchet freak

Réponses:


14

Les tests unitaires appropriés ont une convention de dénomination qui vous aide à identifier immédiatement ce qui a échoué:

public void AddNewCustomer_CustomerExists_ThrowsException()

C'est pourquoi vous avez une assertion par test, de sorte que chaque méthode (et son nom) corresponde à la condition que vous affirmez.

Comme vous l'avez correctement souligné, chaque nouveau test aura un code de configuration similaire. Comme avec tout code, vous pouvez refactoriser le code commun dans sa propre méthode pour réduire ou éliminer la duplication et rendre votre code plus SEC. Certains frameworks de test sont spécifiquement conçus pour vous permettre de mettre ce code d'installation au même endroit .

Dans TDD, aucun test n'est YAGNI, car vous écrivez des tests uniquement en fonction de ce que vous voulez que votre code fasse. Si vous n'en avez pas besoin, vous n'écrirez pas le test.


La refactorisation en une seule méthode semble bonne. Si je devais avoir besoin d'une instance de la classe pour être dans un certain état, je pourrais créer et renvoyer une nouvelle instance de cet objet dans la méthode refactorisée ou comme vous suggérez d'utiliser les méthodes fournies par le framework de test pour la configuration initiale du test.
Korey Hinton

sur votre dernier point, je suis sûr que je pourrais écrire des tests de fonctionnalité que j'aimerais que le code ait, plutôt que la fonctionnalité dont il avait besoin
joelb

5

Les tests à assertion simple violent-ils donc DRY?

Non, mais cela favorise la violation.

Cela dit, une bonne conception orientée objet a tendance à sortir de la fenêtre pour les tests unitaires - principalement pour une bonne raison. Il est plus important que les tests unitaires soient isolés les uns des autres afin que le test puisse être interrogé isolément et, le cas échéant, corrigé en toute confiance que vous ne casserez pas les autres tests. Fondamentalement, la justesse et la lisibilité des tests sont plus importantes que leur taille ou leur maintenabilité.

Franchement, je n'ai jamais été fan de la seule assertion par règle de test pour les raisons que vous décrivez: cela conduit à beaucoup de code passe-partout difficile à lire, facile à mal bouillir et difficile à bien réparer si vous refactorisez (ce qui vous pousse à refactoriser moins).

Si une fonction est censée retourner une liste de "foo" et "bar" pour une entrée donnée, mais dans n'importe quel ordre, il est tout à fait correct d'utiliser deux assertions pour vérifier que les deux sont dans le jeu de résultats. Là où vous avez des problèmes, c'est quand un seul test vérifie deux entrées ou deux effets secondaires et que vous ne savez pas lequel des deux a provoqué l'échec.

Je le vois comme une variation du principe de responsabilité unique: il ne devrait y avoir qu'une seule chose peut faire échouer un test, et dans un monde idéal, ce changement ne devrait casser qu'un seul test.

Mais en fin de compte, c'est un compromis. Êtes-vous plus susceptible de passer plus de temps à maintenir tout le code copier-coller, ou passerez-vous plus de temps à rechercher les causes profondes lorsque les tests peuvent être interrompus par plusieurs sources. Tant que vous écrivez -certains- tests, cela n'a probablement pas trop d'importance. Malgré mon dédain pour les tests à assertion simple, j'ai tendance à pécher par excès de tests. Votre kilométrage peut varier.


1
Pour les tests, il est plus important d'être DAMP que DRY (Phrases descriptives et significatives).
Jörg W Mittag

2

Non, cela semble être la façon dont vous le faites. Sauf si vous avez trouvé une référence notable où ils prétendent que c'est une bonne pratique.

Utilisez un appareil de test (bien que dans la terminologie XUnit l'ensemble de tests, la configuration et le démontage soient l'appareil), c'est-à-dire une configuration ou des exemples qui s'appliquent à tous vos tests.

Utilisez des méthodes comme vous le feriez normalement pour structurer votre code. Lors de la refactorisation des tests, l'habituel TDD Red-Green-Refactor ne s'applique pas, mais plutôt "Refactoring in the Red". C'est,

  1. casser délibérément votre test,
  2. faites votre refactoring
  3. réparer votre test

De cette façon, vous savez que les tests donnent toujours des résultats positifs et négatifs.

Il existe plusieurs formats standard pour les tests. Par exemple, Réorganiser, Agir, Affirmer ou Étant donné quand, alors (BDD) . Envisagez d'utiliser une fonction distincte pour chaque étape. Vous devriez être en mesure d'appeler la fonction pour réduire le passe-partout.

En utilisant notre site, vous reconnaissez avoir lu et compris notre politique liée aux cookies et notre politique de confidentialité.
Licensed under cc by-sa 3.0 with attribution required.