J'ai parcouru la documentation de phpunit et suis tombé sur la citation suivante:
Vous pouvez toujours écrire plus de tests. Cependant, vous constaterez rapidement que seule une fraction des tests que vous pouvez imaginer est réellement utile. Ce que vous voulez, c'est écrire des tests qui échouent même si vous pensez qu'ils doivent fonctionner, ou des tests qui réussissent même si vous pensez qu'ils doivent échouer. Une autre façon de penser est en termes de coûts / avantages. Vous voulez écrire des tests qui vous rembourseront avec des informations. --Erich Gamma
Cela m'a fait me demander. Comment déterminez-vous ce qui rend un test unitaire plus utile qu'un autre, mis à part ce qui est indiqué dans cette citation sur les coûts / avantages. Comment décidez-vous pour quelle partie de votre code vous créez des tests unitaires? Je demande cela parce qu'une autre de ces citations a également dit:
Donc, s'il ne s'agit pas de tests, de quoi s'agit-il? Il s'agit de déterminer ce que vous essayez de faire avant de vous lancer à moitié armé pour essayer de le faire. Vous écrivez une spécification qui décrit un petit aspect du comportement sous une forme concise, non ambiguë et exécutable. C'est si simple. Est-ce que cela signifie que vous écrivez des tests? Non, cela signifie que vous écrivez des spécifications de ce que votre code devra faire. Cela signifie que vous spécifiez le comportement de votre code à l'avance. Mais pas très en avance sur le temps. En fait, le mieux est d’écrire le code, c’est parce que vous avez le maximum d’informations sous la main. Comme TDD bien fait, vous travaillez par petites étapes ... en spécifiant un petit aspect du comportement à la fois, puis en le mettant en œuvre. Lorsque vous réalisez qu'il s'agit de spécifier un comportement et non d'écrire des tests, votre point de vue change. Soudainement, l'idée d'avoir une classe de test pour chacune de vos classes de production est ridiculement limitante. Et l'idée de tester chacune de vos méthodes avec sa propre méthode de test (dans une relation 1-1) sera risible. --Dave Astels
La partie importante de cela est
* Et l'idée de tester chacune de vos méthodes avec sa propre méthode de test (dans une relation 1-1) sera risible. *
Donc, si créer un test pour chaque méthode est "risible", comment / quand avez-vous choisi ce pour quoi vous écrivez des tests?
la source
Réponses:
Combien de tests par méthode?
Eh bien, le maximum théorique et hautement impraticable est la complexité de N-Path (supposons que les tests couvrent tous différentes manières dans le code;)). Le minimum est UN !. Par méthode publique , il ne teste pas les détails de l'implémentation, mais uniquement les comportements externes d'une classe (renvoie des valeurs et appelle d'autres objets).
Vous citez:
et ensuite demander:
Mais je pense que vous avez mal compris l'auteur ici:
L'idée d'avoir
one test method
parone method in the class to test
ce que l'auteur appelle « risible ».(Pour moi au moins) Il ne s'agit pas de "moins" mais de "plus"
Alors laissez-moi reformuler comme je l'ai compris:
Et l'idée de tester chacune de vos méthodes avec ONLY ONE METHOD (sa propre méthode de test dans une relation 1-1) sera risible.
Pour citer à nouveau votre devis:
Lorsque vous pratiquez le TDD, vous ne pensez pas :
J'ai une méthode
calculateX($a, $b);
et il faut un testtestCalculcateX
qui teste TOUT sur la méthode.Ce que TDD vous dit, c'est de réfléchir à ce que votre code DEVRAIT FAIRE :
J'ai besoin de calculer la plus grande des deux valeurs ( premier cas de test! ), Mais si $ a est inférieur à zéro, il devrait générer une erreur ( deuxième cas de test! ) Et si $ b est inférieur à zéro, il devrait ... ( troisième cas de test! ) et ainsi de suite.
Vous voulez tester des comportements, pas seulement des méthodes uniques sans contexte.
De cette façon, vous obtenez une suite de tests qui est une documentation pour votre code et explique VRAIMENT ce qu’elle est censée faire, peut-être même pourquoi :)
Comment décidez-vous pour quelle partie de votre code vous créez des tests unitaires?
Eh bien, tout ce qui finit dans le référentiel ou n'importe où près de la production nécessite un test. Je ne pense pas que l'auteur de vos citations serait en désaccord avec cela car j'ai essayé de le dire plus haut.
Si vous n'avez pas de test pour cela, il devient beaucoup plus difficile (plus coûteux) de changer le code, surtout si ce n'est pas vous qui le modifiez.
TDD est un moyen de vous assurer que vous avez des tests pour TOUT, mais tant que vous écrivez les tests, tout va bien. D'habitude, les écrire le même jour est utile, car vous n'allez pas le faire plus tard, n'est-ce pas? :)
Réponse aux commentaires:
Il y a trois choses que ces méthodes peuvent appeler:
Méthodes publiques d'autres classes
Nous pouvons simuler d'autres classes afin d'y définir l'état. Nous contrôlons le contexte afin que ce ne soit pas un problème là-bas.
* Méthodes protégées ou privées sur le même *
Tout ce qui ne fait pas partie de l'API publique d'une classe n'est généralement pas testé directement.
Vous voulez tester le comportement et non l'implémentation et si une classe fait tout son travail dans une grande méthode publique ou dans plusieurs méthodes protégées plus petites appelées implémentation . Vous voulez pouvoir CHANGER ces méthodes protégées SANS toucher vos tests. Parce que vos tests vont casser si votre code change de comportement! C’est ce que vos tests sont là pour vous dire quand vous cassez quelque chose :)
Méthodes publiques sur la même classe
Cela n'arrive pas très souvent le fait? Et si cela ressemble à l'exemple suivant, il y a plusieurs façons de gérer cela:
Que les setters existent et ne font pas partie de la signature de la méthode execute est un autre sujet;)
Ce que nous pouvons tester ici, c'est si les exécutions explosent lorsque nous définissons des valeurs erronées. Cela
setBla
lève une exception lorsque vous passez une chaîne peut être testé séparément, mais si nous voulons tester que ces deux valeurs autorisées (12 & 14) ne fonctionnent pas ensemble (pour une raison quelconque) que celle d'un cas de test.Si vous voulez une "bonne" suite de tests, vous pouvez, en php, peut-être (!) Ajouter une
@covers Stuff::execute
annotation pour vous assurer que vous ne générez une couverture de code que pour cette méthode et que les autres éléments qui ne font que configurer doivent être testés séparément vous voulez que).Le problème est donc le suivant: vous devez peut-être d'abord créer une partie du monde environnant, mais vous devriez être capable d'écrire des scénarios de test significatifs qui ne couvrent généralement qu'une ou deux fonctions réelles (les ajusteurs ne comptent pas ici). Le reste peut être moqué à l'éther ou être testé en premier et ensuite invoqué (voir
@depends
)* Remarque: La question a été migrée depuis SO et concernait initialement PHP / PHPUnit, c'est pourquoi l'exemple de code et les références proviennent du monde php. Je pense que cela s'applique également à d'autres langages, car phpunit ne diffère pas beaucoup des autres xUnit. cadres de test.
la source
Les tests et les tests unitaires ne sont pas la même chose. Le test unitaire est un sous-ensemble très important et intéressant du test global. Je dirais que l'objectif des tests unitaires nous fait penser à ce type de tests d'une manière qui contredit quelque peu les citations ci-dessus.
Premièrement, si nous suivons le TDD ou même le DTH (c’est-à-dire que nous développons et testons en étroite harmonie), nous utilisons les tests que nous écrivons afin de nous assurer que notre conception est correcte. En réfléchissant sur les cas critiques et en écrivant des tests en conséquence, nous évitons que les bugs ne se manifestent. Nous écrivons donc des tests que nous nous attendons à réussir (OK dès le début du TDD, ils échouent, mais ce n'est qu'un artefact de commande, le code est terminé, nous nous attendons à ce qu'il soit adopté, et la plupart le sont, car vous avez pensé au code.
Deuxièmement, les tests unitaires prennent tout leur sens lorsque nous procédons à une refactorisation. Nous modifions notre mise en œuvre, mais nous attendons que les réponses restent les mêmes. Le test unitaire constitue notre protection contre la rupture de notre contrat d'interface. Donc, encore une fois, nous attendons les tests.
Cela implique que pour notre interface publique, qui est vraisemblablement stable, nous avons besoin d'une traçabilité claire afin que nous puissions voir que chaque méthode publique est testée.
Pour répondre à votre question explicite: Les tests unitaires pour interface publique ont une valeur.
édité en réponse commentaire:
Tester des méthodes privées? Oui, nous devrions le faire, mais si nous devions ne pas tester quelque chose, nous ferions un compromis. Après tout, si les méthodes publiques fonctionnent, est-ce que ces bugs dans le domaine privé peuvent être si importants? De manière pragmatique, le désabonnement a tendance à se produire dans le domaine privé, vous travaillez dur pour maintenir votre interface publique, mais si les éléments dont vous dépendez changent, les éléments privés risquent de changer. À un moment donné, il peut s'avérer difficile de maintenir les tests internes. Cet effort est-il bien dépensé?
la source
Les tests unitaires doivent faire partie d'une stratégie de test plus large. Je suis ces principes pour choisir quels types de tests écrire et quand:
Concentrez-vous sur la rédaction de tests de bout en bout. Vous couvrez plus de code par test qu'avec les tests unitaires et obtenez ainsi plus de tests pour votre argent. Faites-en la validation automatique du système dans son ensemble.
Passez à la rédaction de tests unitaires autour de pépites de logique compliquée. Les tests unitaires valent leur pesanteur dans les situations où il serait difficile de déboguer des tests de bout en bout ou difficiles à écrire pour obtenir une couverture de code adéquate.
Attendez que l'API que vous testez soit stable pour écrire l'un ou l'autre type de test. Vous voulez éviter de refactoriser à la fois votre implémentation et vos tests.
Rob Ashton a rédigé un excellent article sur ce sujet, dont je me suis largement inspiré pour énoncer les principes ci-dessus.
la source
J'ai tendance à suivre une approche différente des tests unitaires qui semble bien fonctionner. Au lieu de penser à un test unitaire comme à "Tester un comportement", je le considère plutôt comme "une spécification que mon code doit suivre". De cette façon, vous pouvez fondamentalement déclarer qu'un objet doit se comporter d'une certaine manière et, étant donné que vous supposez qu'ailleurs dans votre programme, vous pouvez être presque certain qu'il est relativement exempt de bogues.
Si vous écrivez une API publique, cela est extrêmement précieux. Cependant, vous aurez toujours besoin d’une bonne dose de tests d’intégration de bout en bout, car une couverture à 100% des tests unitaires n’en vaut généralement pas la peine et ratera ce que la plupart des gens considèrent comme "non testable" par des méthodes de tests unitaires (moqueurs, etc)
la source