Je viens de sortir de l'université et commence l'université quelque part la semaine prochaine. Nous avons vu des tests unitaires, mais nous ne les avons pas beaucoup utilisés; et tout le monde en parle, alors je me suis dit que je devrais peut-être en faire.
Le problème est que je ne sais pas quoi tester. Devrais-je tester le cas commun? L'affaire Edge? Comment savoir qu'une fonction est correctement couverte?
J'ai toujours le sentiment terrible que, si un test prouve qu'une fonction fonctionne pour un cas donné, il est totalement inutile de prouver que la fonction fonctionne, point à la ligne.
Réponses:
Ma philosophie personnelle a donc été:
la source
Parmi la pléthore de réponses à ce jour, personne n’a abordé le partitionnement de l’équivalence et l’ analyse des valeurs limites , considérations essentielles dans la réponse à la question posée. Toutes les autres réponses, bien qu'utiles, sont qualitatives, mais il est possible - et préférable - d'être quantitatives ici. @fishtoaster fournit des indications concrètes, jetant un coup d'œil sous le couvert de la quantification de test, mais le partitionnement par équivalence et l'analyse des valeurs limites nous permettent de faire mieux.
Dans la partition d'équivalence , vous divisez l'ensemble des entrées possibles en groupes en fonction des résultats attendus. Toute entrée d'un groupe donnera des résultats équivalents, de tels groupes s'appellent des classes d'équivalence . (Notez que des résultats équivalents ne signifient pas des résultats identiques.)
Comme exemple simple, considérons un programme qui devrait transformer des caractères ASCII minuscules en caractères majuscules. Les autres personnages doivent subir une transformation d'identité, c'est-à-dire rester inchangés. Voici une ventilation possible en classes d'équivalence:
La dernière colonne indique le nombre de tests si vous les énumérez tous. Techniquement, selon la règle 1 de @ fishtoaster, vous incluez 52 cas tests - tous ceux des deux premières lignes indiquées ci-dessus entrent dans le "cas commun". La règle 2 de @ fishtoaster ajoute également tout ou partie des rangées 3 et 4 ci-dessus. Toutefois, avec le test de partitionnement par équivalence, un seul cas de test dans chaque classe d'équivalence est suffisant. Si vous choisissez "a" ou "g" ou "w", vous testez le même chemin de code. Ainsi, vous avez un total de 4 cas de test au lieu de 52+.
L'analyse de la valeur limite recommande un léger raffinement: elle suggère essentiellement que tous les membres d'une classe d'équivalence ne sont pas équivalents. C'est-à-dire que les valeurs aux limites doivent également être considérées comme dignes d'un cas test à part entière. (Une erreur simple en est la fameuse erreur off-by-one !) Ainsi, pour chaque classe d'équivalence, vous pouvez avoir 3 entrées de test. En regardant le domaine d'entrée ci-dessus - et avec une certaine connaissance des valeurs ASCII -, je pourrais arriver avec ces entrées de scénario de test:
(Dès que vous obtenez plus de 3 valeurs limites, cela suggère que vous voudriez peut-être repenser vos délinéations de classes d'équivalence d'origine, mais c'était assez simple pour que je ne revienne pas les réviser.) Ainsi, l'analyse des valeurs limites nous amène à 17 cas de test - avec une confiance élevée en couverture complète - comparés à 128 cas de test exhaustifs. (Sans compter que la combinatoire indique qu'un test exhaustif est tout simplement irréalisable pour toute application réelle!)
la source
Mon opinion n’est probablement pas trop populaire. Mais je vous suggère d'être économique avec les tests unitaires. Si vous avez trop de tests unitaires, vous pouvez facilement passer plus de la moitié de votre temps à entretenir les tests plutôt qu’à coder.
Je vous suggère d’écrire des tests sur des choses qui vous font mal ou sur des choses qui sont très importantes et / ou élémentaires. Les tests unitaires IMHO ne remplacent pas une bonne ingénierie et un codage défensif. Actuellement, je travaille sur un projet plus ou moins inutilisable. C'est vraiment stable mais ça fait mal au refactor. En fait, personne n’a touché à ce code en un an et la pile de logiciels sur laquelle il est basé a 4 ans. Pourquoi? Parce qu’il est encombré de tests unitaires, pour être précis: Tests unitaires et tests d’intégration automatisés. (Vous avez déjà entendu parler de concombre, etc.) Et voici la meilleure partie: ce logiciel (pourtant) inutilisable a été développé par une entreprise dont les employés sont des pionniers sur la scène du développement piloté par les tests. :RÉ
Donc ma suggestion est:
Commencez à écrire des tests après avoir développé le squelette de base, sinon la refactorisation peut être douloureuse. En tant que développeur qui développe pour les autres, vous n’obtenez jamais les exigences requises dès le départ.
Assurez-vous que vos tests unitaires peuvent être effectués rapidement. Si vous avez des tests d'intégration (comme le concombre), c'est bon s'ils prennent un peu plus longtemps. Mais les longs tests ne sont pas amusants, croyez-moi. (Les gens oublient toutes les raisons pour lesquelles le C ++ est devenu moins populaire ...)
Laissez ce matériel TDD aux experts TDD.
Et oui, parfois vous vous concentrez sur les cas extrêmes, parfois sur les cas courants, selon l'endroit où vous vous attendez à l'inattendu. Cependant, si vous attendez toujours l'imprévu, vous devez vraiment repenser votre flux de travail et votre discipline. ;-)
la source
Leave this TDD stuff to the TDD-experts
.Si vous testez d'abord avec Test Driven Development, votre couverture sera supérieure ou égale à 90%, car vous n'ajouterez pas de fonctionnalité sans avoir au préalable écrit un test d'unité défaillant.
Si vous ajoutez des tests après coup, je ne saurais trop vous recommander d'obtenir une copie de Working Efficient With Legacy Code de Michael Feathers et de jeter un coup d'œil à certaines des techniques permettant d'ajouter des tests à votre code et des méthodes de refactorisation de votre code. pour le rendre plus testable.
la source
The problem is, I don't know _what_ to test
Si vous commencez à suivre les pratiques de développement piloté par les tests , elles vous guideront tout au long du processus et sauront naturellement quoi tester. Quelques endroits pour commencer:
Les tests viennent en premier
N'écrivez jamais de code avant d'écrire les tests. Voir Red-Green-Refactor-Repeat pour une explication.
Écrire des tests de régression
Chaque fois que vous rencontrez un bogue, écrivez un testcase et assurez-vous qu'il échoue . À moins que vous ne puissiez reproduire un bogue dans un cas de test défaillant, vous ne l'avez pas vraiment trouvé.
Red-Green-Refactor-Repeat
Rouge : commencez par écrire un test élémentaire pour le comportement que vous essayez d'implémenter. Pensez à cette étape au moment d’écrire un exemple de code qui utilise la classe ou la fonction sur laquelle vous travaillez. Assurez-vous qu'il compile / n'a pas d'erreur de syntaxe et qu'il échoue . Cela devrait être évident: vous n'avez écrit aucun code, il doit donc échouer, n'est-ce pas? La chose importante à apprendre ici est que, à moins que le test échoue au moins une fois, vous ne pouvez jamais être sûr que s'il réussit, il le fait à cause de quelque chose que vous avez commis pour une raison fictive.
Vert : écrivez le code le plus simple et le plus stupide qui fait passer le test. N'essayez pas d'être intelligent. Même si vous voyez qu'il y a un cas évident de bord, mais que le test en tient compte, n'écrivez pas de code pour le gérer (mais n'oubliez pas le cas de bord: vous en aurez besoin plus tard). L'idée est que chaque élément de code que vous écrivez, chaque élément
if
,try: ... except: ...
devrait être justifié par un scénario de test. Le code n'a pas besoin d'être élégant, rapide ou optimisé. Vous voulez juste que le test passe.Refactor : Nettoyez votre code, obtenez les noms de méthodes correctement. Voir si le test est toujours en train de passer. Optimiser. Exécutez le test à nouveau.
Répétez : vous vous souvenez du cas limite que le test n'a pas couvert, non? Alors, maintenant c'est son grand moment. Ecrivez un cas de test qui couvre cette situation, regardez-le échouer, écrivez du code, voyez-le passer, refactor.
Testez votre code
Vous travaillez sur un morceau de code spécifique et c'est exactement ce que vous voulez tester. Cela signifie que vous ne devriez pas tester les fonctions de la bibliothèque, la bibliothèque standard ou votre compilateur. Aussi, essayez d'éviter de tester le "monde". Cela inclut: l’appel d’API Web externes, l’utilisation de bases de données intensives, etc. Chaque fois que vous pouvez essayer de vous en moquer (créez un objet qui suit la même interface, mais renvoie des données statiques prédéfinies).
la source
Pour les tests unitaires, commencez par vérifier que le logiciel répond aux objectifs pour lesquels il a été conçu. Ce devrait être le tout premier cas que vous écrivez. Si une partie de la conception est "elle devrait renvoyer une exception si vous passez en courrier indésirable", testez-la aussi car elle fait partie de la conception.
Commence avec ça. À mesure que vous maîtriserez les tests les plus élémentaires, vous commencerez à savoir si cela est suffisant ou non, et vous verrez d'autres aspects de votre code nécessitant des tests.
la source
La réponse courante est de "tester tout ce qui pourrait casser" .
Quoi de trop simple pour casser? Champs de données, accesseurs de propriétés mortes cérébrales et frais généraux similaires. Tout le reste met probablement en œuvre une partie identifiable d'une exigence et peut bénéficier d'un test.
Bien entendu, votre kilométrage et les pratiques de votre environnement de travail peuvent varier.
la source