En recherchant les meilleures pratiques de tests unitaires pour aider à élaborer des lignes directrices pour mon organisation, je me suis demandé s'il était préférable ou utile de séparer les montages de test (classes de test) ou de conserver tous les tests d'une seule classe dans un seul fichier.
Fwiw, je me réfère aux "tests unitaires" au sens pur, ce sont des tests en boîte blanche ciblant une seule classe, une assertion par test, toutes les dépendances moquées, etc.
Un exemple de scénario est une classe (appelez-la Document) qui a deux méthodes: CheckIn et CheckOut. Chaque méthode implémente différentes règles, etc. qui contrôlent leur comportement. En suivant la règle d'une assertion par test, j'aurai plusieurs tests pour chaque méthode. Je peux soit placer tous les tests dans une seule DocumentTests
classe avec des noms comme CheckInShouldThrowExceptionWhenUserIsUnauthorized
et CheckOutShouldThrowExceptionWhenUserIsUnauthorized
.
Ou, je pourrais avoir deux classes de test distinctes: CheckInShould
et CheckOutShould
. Dans ce cas, mes noms de test seraient raccourcis, mais ils seraient organisés de sorte que tous les tests pour un comportement (méthode) spécifique soient ensemble.
Je suis sûr qu'il y a des avantages et des inconvénients à l'une ou l'autre approche et je me demande si quelqu'un a opté pour plusieurs fichiers et, si oui, pourquoi? Ou, si vous avez opté pour l'approche à fichier unique, pourquoi pensez-vous que c'est mieux?
la source
testResponseContainsSuccessTrue()
,testResponseContainsMyData()
ettestResponseStatusCodeIsOk()
. Vous les avez en un seultestResponse()
qui a trois affirme:assertEquals(200, response.status)
,assertEquals({"data": "mydata"}, response.data)
etassertEquals(true, response.success)
Réponses:
C'est rare, mais parfois il est logique d'avoir plusieurs classes de test pour une classe donnée à tester. En général, je le fais lorsque différentes configurations sont requises et partagées sur un sous-ensemble de tests.
la source
Je ne vois pas vraiment de raison impérieuse pour laquelle vous diviseriez un test pour une seule classe en plusieurs classes de test. Étant donné que l'idée de conduite doit être de maintenir la cohésion au niveau de la classe, vous devez également vous efforcer de le faire au niveau du test. Juste quelques raisons aléatoires:
la source
Si vous êtes obligé de fractionner les tests unitaires d'une classe sur plusieurs fichiers, cela peut indiquer que la classe elle-même est mal conçue. Je ne peux penser à aucun scénario où il serait plus avantageux de diviser les tests unitaires pour une classe qui adhère raisonnablement au principe de responsabilité unique et aux autres meilleures pratiques de programmation.
De plus, avoir des noms de méthode plus longs dans les tests unitaires est acceptable, mais si cela vous dérange, vous pouvez toujours repenser votre convention de dénomination des tests unitaires pour raccourcir les noms.
la source
Un de mes arguments contre la séparation des tests en plusieurs classes est qu'il devient plus difficile pour les autres développeurs de l'équipe (en particulier ceux qui ne sont pas aussi avertis des tests) de localiser les tests existants ( Gee, je me demande s'il existe déjà un test pour cela ? Je me demande où ce serait? ) et aussi où mettre de nouveaux tests ( je vais écrire un test pour cette méthode dans cette classe, mais je ne sais pas trop si je devrais les mettre dans un nouveau fichier, ou un fichier existant) un? )
Dans le cas où différents tests nécessitent des configurations radicalement différentes, j'ai vu certains praticiens du TDD mettre le "fixture" ou le "setup" dans différentes classes / fichiers, mais pas les tests eux-mêmes.
la source
J'aimerais pouvoir me souvenir / trouver le lien où la technique que j'ai choisie d'adopter a été démontrée pour la première fois. Essentiellement, je crée une classe abstraite unique pour chaque classe sous test qui contient des montages de test imbriqués (classes) pour chaque membre testé. Cela fournit la séparation initialement souhaitée mais conserve tous les tests dans le même fichier pour chaque emplacement. De plus, cela génère un nom de classe qui permet un regroupement et un tri faciles dans le lanceur de test.
Voici un exemple de la façon dont cette approche est appliquée à mon scénario d'origine:
Il en résulte que les tests suivants apparaissent dans la liste des tests:
Au fur et à mesure que de nouveaux tests sont ajoutés, ils sont facilement regroupés et triés par nom de classe, ce qui conserve également tous les tests de la classe sous test répertoriés.
Un autre avantage de cette approche que j'ai appris à exploiter est la nature orientée objet de cette structure signifie que je peux définir du code à l'intérieur des méthodes de démarrage et de nettoyage de la classe de base DocumentTests qui seront partagées par toutes les classes imbriquées ainsi qu'à l'intérieur de chacune classe imbriquée pour les tests qu'elle contient.
la source
Essayez de penser l'inverse pendant une minute.
Pourquoi auriez-vous une seule classe de test par classe? Faites-vous un test de classe ou un test unitaire? Dépendez-vous même des cours ?
Un test unitaire est censé tester un comportement spécifique, dans un contexte spécifique. Le fait que votre classe ait déjà un
CheckIn
moyen, il devrait y avoir un comportement qui en a besoin en premier lieu.Que pensez-vous de ce pseudo-code:
Maintenant, vous ne testez pas directement la
checkIn
méthode. Vous testez plutôt un comportement (qui consiste à s'enregistrer heureusement;)), et si vous devez le refactoriserDocument
et le diviser en différentes classes, ou y fusionner une autre classe, vos fichiers de test sont toujours cohérents, ils ont toujours du sens puisque vous ne changez jamais la logique lors de la refactorisation, seulement la structure.Un fichier de test pour une classe, il est tout simplement plus difficile de refactoriser chaque fois que vous en avez besoin, et les tests ont également moins de sens en termes de domaine / logique du code lui-même.
la source
En général, je recommanderais de considérer les tests comme testant un comportement de la classe plutôt qu'une méthode. C'est-à-dire que certains de vos tests peuvent avoir besoin d'appeler les deux méthodes sur la classe afin de tester un comportement attendu.
Habituellement, je commence avec une classe de tests unitaires par classe de production, mais je peux éventuellement diviser cette classe de tests unitaires en plusieurs classes de tests en fonction du comportement qu'ils testent. En d'autres termes, je recommanderais de ne pas diviser la classe de test en CheckInShould et CheckOutShould, mais plutôt de diviser par le comportement de l'unité testée.
la source
1. Réfléchissez à ce qui risque de mal tourner
Pendant le développement initial, vous savez assez bien ce que vous faites et les deux solutions fonctionneront probablement bien.
Cela devient plus intéressant lorsqu'un test échoue après un changement beaucoup plus tard. Il y a deux possibilités:
CheckIn
(ouCheckOut
). Encore une fois, le fichier unique ainsi que la solution à deux fichiers sont OK dans ce cas.CheckIn
etCheckOut
(et peut-être leurs tests) d'une manière qui a du sens pour chacun d'eux, mais pas pour les deux ensemble. Vous avez rompu la cohérence de la paire. Dans ce cas, la division des tests en deux fichiers rendra la compréhension du problème plus difficile.2. Réfléchissez aux tests utilisés
Les tests ont deux objectifs principaux:
Donc?
Ces deux perspectives suggèrent que le maintien des tests ensemble peut aider, mais ne fera pas de mal.
la source