Déclenché par ce fil , je pense (encore une fois) à l'idée d'utiliser enfin des tests unitaires dans mes projets. Quelques affiches disent: "Les tests sont cool, s’ils sont de bons tests". Ma question maintenant: quels sont les "bons" tests?
Dans mes applications, l'essentiel est souvent une sorte d'analyse numérique, qui dépend de grandes quantités de données observées et qui aboutit à une fonction d'ajustement qui peut être utilisée pour modéliser ces données. J'ai trouvé particulièrement difficile de construire des tests pour ces méthodes, car le nombre d'entrées et de résultats possibles est trop important pour tester tous les cas, et les méthodes elles-mêmes sont souvent assez longues et ne peuvent pas être refactorisées facilement sans sacrifier les performances. Je suis particulièrement intéressé par les "bons" tests pour ce type de méthode.
la source
Réponses:
L'art des tests unitaires a la signification suivante à propos des tests unitaires:
et ajoute ensuite qu'il devrait être entièrement automatisé, digne de confiance, lisible et maintenable.
Je vous recommande fortement de lire ce livre si vous ne l'avez pas déjà fait.
À mon avis, tout cela est très important, mais les trois derniers (dignes de confiance, lisibles et gérables), en particulier, comme si vos tests avaient ces trois propriétés, votre code les avait aussi.
la source
It should run at the push of a button
, est - ce que cela signifie qu'un test unitaire ne doit pas exiger soit des conteneurs (serveur d'application) en cours d' exécution (pour l'unité testée) ou une connexion de ressources (comme DB, les services Web externes , etc.)? Je ne comprends pas très bien quelles parties d'une application doivent faire l'objet d'un test unitaire ou non. On m'a dit que les tests unitaires ne devraient pas dépendre de la connexion à la base de données et des conteneurs en cours d'exécution; il se peut que des maquettes soient utilisées.Un bon test unitaire ne reflète pas la fonction testée.
A titre d'exemple très simplifié, considérons que vous avez une fonction qui renvoie une moyenne de deux int. Le test le plus complet appelle la fonction et vérifie si le résultat est en fait une moyenne. Cela n’a aucun sens: vous mettez en miroir (répliquez) la fonctionnalité que vous testez. Si vous faites une erreur dans la fonction principale, vous ferez la même erreur lors du test.
En d'autres termes, si vous vous retrouvez à reproduire la fonctionnalité principale du test unitaire, c'est probablement le signe que vous perdez votre temps.
la source
Un bon test unitaire est essentiellement la spécification sous forme exécutable:
J'ai trouvé que Test-Driven-Development était très bien adapté aux routines de bibliothèque car vous écrivez essentiellement l'API d'abord, puis ALORS la mise en œuvre réelle.
la source
pour TDD, de "bons" tests, des fonctionnalités de test souhaitées par le client ; les fonctionnalités ne correspondent pas nécessairement aux fonctions, et le développeur ne doit pas créer des scénarios de test en vase clos
dans votre cas - je suppose - la «fonctionnalité» est que la fonction d'ajustement modélise les données d'entrée avec une certaine tolérance aux erreurs. Puisque je n'ai aucune idée de ce que vous faites réellement, je fabrique quelque chose; j'espère que c'est analgous.
Exemple d'histoire:
Alors vous allez parler aux pilotes (et à l'ordinateur de ciblage, s'il est sensible). Tout d'abord, vous parlez de ce qui est «normal», puis de l'anormal. Vous découvrez ce qui compte vraiment dans ce scénario, ce qui est commun, ce qui est peu probable et ce qui est simplement possible.
Supposons que vous disposiez normalement d'une fenêtre d'une demi-seconde sur sept canaux de données de télémétrie: vitesse, tangage, roulis, lacet, vecteur cible, taille cible et vitesse cible, et que ces valeurs soient constantes ou changent linéairement. De manière anormale, vous pouvez avoir moins de canaux et / ou les valeurs peuvent changer rapidement. Alors, ensemble, vous obtenez des tests tels que:
Vous avez peut-être remarqué qu'il n'y a pas de scénario pour la situation particulière décrite dans l'histoire. Après avoir discuté avec le client et d’autres parties prenantes, il s’avère que cet objectif n’était qu’un exemple hypothétique. Les vrais tests sont issus de la discussion qui a suivi. Cela peut arriver. L’histoire doit être réécrite, mais pas nécessairement [car l’histoire n’est qu’un espace réservé pour une conversation avec le client].
la source
Créez des tests pour les cas extrêmes, comme un ensemble de test contenant uniquement le nombre minimum d'entrées (1 ou 0 possible) et quelques cas standard. Ces tests unitaires ne remplacent pas non plus les tests de réception approfondis.
la source
J'ai vu de nombreux cas où des personnes investissent énormément d'effort pour écrire des tests de code rarement saisi et ne pas écrire de tests pour du code saisi fréquemment.
Avant de vous préparer à passer des tests, vous devriez examiner un graphique d’appel afin de vous assurer de planifier une couverture adéquate.
De plus, je ne crois pas à écrire des tests juste pour dire "Ouais, on teste ça". Si j'utilise une bibliothèque qui est restée immuable et restera immuable, je ne vais pas perdre une journée à écrire des tests pour m'assurer que les entrailles d'une API qui ne changera jamais fonctionneront comme prévu, même si certaines parties de celle-ci marquent des points. haut sur un graphique d'appel. Les tests utilisant cette librairie (mon propre code) le prouvent.
la source
Pas tout à fait aussi TDD, mais une fois que vous êtes passé au contrôle qualité, vous pouvez améliorer vos tests en configurant des scénarios de test afin de reproduire tous les bogues rencontrés au cours du processus de contrôle qualité. Cela peut être particulièrement utile lorsque vous entrez dans un support à long terme et que vous commencez à vous rendre dans un endroit où vous risquez que des personnes réintroduisent par inadvertance d'anciens bugs. Avoir un test en place pour capturer cela est particulièrement précieux.
la source
J'essaie de faire en sorte que chaque test ne teste qu'une chose. J'essaie de donner à chaque test un nom tel que shouldDoSomething (). J'essaie de tester le comportement, pas la mise en œuvre. Je teste uniquement des méthodes publiques.
J'ai habituellement un ou plusieurs tests de réussite, puis peut-être une poignée de tests d'échec, par méthode publique.
J'utilise beaucoup les maquettes. Un bon modèle de framework serait probablement très utile, comme PowerMock. Bien que je n'en utilise pas encore.
Si la classe A utilise une autre classe B, j'ajouterais une interface, X, de sorte que A n'utilise pas B directement. Ensuite, je créais une maquette XMockup et l’utilisais au lieu de B dans mes tests. Cela aide vraiment à accélérer l'exécution des tests, à réduire la complexité des tests et à réduire le nombre de tests que j'écris pour A, car je n'ai pas à faire face aux particularités de B. Je peux par exemple tester que A appelle X.someMethod () au lieu d'appeler B.someMethod ().
Gardez votre code de test propre aussi.
Lors de l'utilisation d'une API, telle qu'une couche de base de données, je la simulais et la permettais de générer une exception à chaque opportunité possible sur commande. Je lance ensuite les tests un sans lancer, puis dans une boucle, lançant à chaque fois une exception à la prochaine occasion jusqu'à ce que le test réussisse à nouveau. Un peu comme les tests de mémoire disponibles pour Symbian.
la source
Je vois qu'Andry Lowry a déjà publié les métriques de tests unitaires de Roy Osherove; mais il semble que personne n'ait présenté l'ensemble (complémentaire) que donne Oncle Bob dans Clean Code (132-133). Il utilise l'acronyme FIRST (ici avec mes résumés):
la source