61 votes

La valeur des tests unitaires de haut niveau et des objets fantaisie

Je commence à croire que le test unitaire d'un code de haut niveau, bien écrit, qui nécessite une utilisation intensive d'objets fantaisie, a peu ou pas de valeur. Je me demande si cette affirmation est correcte, ou si quelque chose m'échappe ?

Qu'est-ce que j'entends par haut niveau ? Ce sont les classes et les fonctions situées au sommet de la chaîne alimentaire. Leurs entrées et sorties sont généralement des entrées utilisateur et des interfaces utilisateur. La plupart de leur travail consiste à prendre l'entrée de l'utilisateur et à faire une série d'appels à des entités de niveau inférieur. Elles ont souvent peu ou pas de valeurs de retour significatives.

Qu'est-ce que j'entends par "bien écrit" ? Dans ce cas, je fais référence à un code qui est découplé de ses dépendances (à l'aide d'interfaces et d'injection de dépendances) et qui, ligne par ligne, se situe à un niveau d'abstraction cohérent. Il n'y a pas d'algorithmes délicats, et peu de conditionnels.

I déteste écrire des tests unitaires pour ce type de code. Les tests unitaires consistent presque entièrement en la configuration d'objets fantaisie. Ligne par ligne, le test unitaire se lit presque comme une image miroir de l'implémentation. En fait, j'écris les tests unitaires en regardant l'implémentation. "D'abord j'affirme que cette méthode mock est appelée, ensuite j'affirme que cette méthode mock est appelée...", etc. Je devrais tester le comportement de la méthode, et non pas vérifier qu'elle appelle la bonne séquence de méthodes. . Autre chose : j'ai constaté que ces tests sont extrêmement fragile au remaniement . Si un test est si fragile qu'il se brise complètement et doit être réécrit lorsque le code testé est remanié, alors n'a-t-on pas perdu l'un des principaux avantages des tests unitaires ?

Je ne veux pas que ce message soit considéré comme argumentatif, ou comme une question. Je vais donc formuler ma question directement : Quelle est la manière correcte de tester unitairement le type de code que j'ai décrit, ou est-il entendu que tout n'a pas besoin d'un test unitaire ?

44voto

Robert Harvey Points 103562

D'après mon expérience, plus votre code est de bas niveau (sans être trivial), plus les tests unitaires ont de la valeur, par rapport à l'effort nécessaire pour les écrire. Plus vous montez dans la chaîne alimentaire, plus les tests deviennent élaborés et coûteux.

Les tests unitaires sont essentiels parce qu'ils vous indiquent si vous avez cassé quelque chose pendant le remaniement.

Les tests de niveau supérieur ont leur propre valeur, mais ils ne sont plus appelés tests unitaires ; ils sont appelés tests d'intégration et tests d'acceptation. Les tests d'intégration sont nécessaires car ils permettent de savoir si les différents composants du logiciel fonctionnent bien ensemble.

Les tests d'acceptation sont ceux que le client approuve. Les tests d'acceptation sont généralement écrits par d'autres personnes (pas le programmeur) afin de fournir une perspective différente ; les programmeurs ont tendance à écrire des tests pour ce qui fonctionne, les testeurs essaient de le casser en testant ce qui ne fonctionne pas.

Le mocking n'est utile que pour les tests unitaires. Pour les tests d'intégration et d'acceptation, la simulation est inutile car elle ne permet pas d'exercer les composants réels du système, tels que la base de données et l'infrastructure de communication.

20voto

womp Points 71924

Une parenthèse

Juste pour revenir sur votre déclaration en gras :

"Je devrais tester le comportement de la méthode, et non pas qu'elle appelle la bonne séquence de méthodes"

Le comportement de l'objet sous test est la séquence d'actions qu'elle entreprend. Il s'agit en fait d'un test de "comportement", alors que lorsque vous dites "comportement de la méthode", je pense que vous voulez dire test d'état, c'est-à-dire lui donner une entrée et vérifier la sortie correcte.

Je fais cette distinction parce que certains puristes du BDD vont jusqu'à affirmer qu'il est beaucoup plus significatif de tester ce que votre classe devrait appeler, plutôt que ce que sont les entrées et les sorties, parce que si vous savez parfaitement comment votre système se comporte, alors vos entrées et sorties seront correctes.

Une réponse

Cela dit, personnellement, je n'écris jamais de tests complets pour la couche UI. Si vous utilisez un modèle MVVM, MVP ou MVC pour votre application, alors au niveau d'une "équipe d'un seul développeur", c'est ennuyeux et contre-productif pour moi de le faire. Je peux voir les bogues de l'interface utilisateur, et oui, la simulation des comportements à ce niveau a tendance à être fragile. Je suis beaucoup plus préoccupé par le fait de m'assurer que mon domaine sous-jacent et les couches DAL fonctionnent correctement.

Quoi est de valeur au premier niveau est un test d'intégration. Vous avez une application web ? Au lieu d'affirmer que les méthodes de votre contrôleur retournent un ActionResult (test de peu de valeur), écrivez un test d'intégration qui demande toutes les pages de votre application et s'assure qu'il n'y a pas de 404 ou 403. Exécutez-le une fois à chaque déploiement.

Réponse finale

Je suis toujours la règle des 80/20 avec les tests unitaires. . Pour obtenir ces derniers 20% de couverture au niveau élevé dont vous parlez, vous devrez fournir 80% de vos efforts. Pour mes projets personnels et la plupart de mes projets professionnels, cela ne paie pas.

En bref, je suis d'accord. J'écrirais des tests d'intégration, et j'ignorerais les tests unitaires pour le code que vous décrivez.

3voto

meiguoren Points 114

Je pense que cela dépend fortement de l'environnement. Si vous êtes dans une équipe relativement petite, et que vous pouvez maintenir l'intégrité des tests, alors les parties les plus complexes de votre application devraient avoir des tests unitaires. D'après mon expérience, maintenir l'intégrité des tests dans les grandes équipes est assez difficile, car les tests sont initialement corrects jusqu'à ce qu'ils se cassent inévitablement... à ce moment-là, ils sont soit a) "corrigés" d'une manière qui annule complètement leur utilité, soit b) rapidement commentés.

L'objectif principal des tests Mock semble être que les managers puissent affirmer que la métrique de couverture de code est à Foo%.... donc tout doit fonctionner ! Le seul cas exceptionnel où ils peuvent être utiles est lorsque vous avez besoin de tester une classe qu'il est très difficile de recréer authentiquement (tester une classe d'action dans Struts, par exemple).

Je suis un grand partisan de l'écriture de tests bruts. Du vrai code, avec de vrais objets. Le code à l'intérieur des méthodes changera avec le temps, mais l'objectif et donc le comportement global ne changent généralement pas.

0voto

Robert Koritnik Points 45499

Si vous faites du TDD, vous ne devez pas écrire les tests après l'implémentation mais plutôt l'inverse. De cette façon, vous éviterez également le problème de la conformité d'un test au code écrit. Vous devez probablement tester certains appels de méthode au sein de ces unités, mais pas leur séquence (si cela n'est pas impératif pour le problème du domaine - processus d'entreprise).

Et parfois, il est parfaitement possible de ne pas écrire de test pour une certaine méthode.

0voto

RHSeeger Points 9217

En général, je considère que le test de ce type de méthode/commande est mûr pour le niveau de test d'intégration. Plus précisément, je "teste unitairement" les petites commandes de bas niveau qui n'ont (généralement) pas d'effets secondaires. Si je veux vraiment tester unitairement quelque chose qui ne correspond pas à ce modèle, la première chose que je fais est de voir si je peux le remanier/redessiner pour qu'il corresponde.

Au niveau supérieur, celui des tests d'intégration (et/ou de système), je teste les éléments qui ont des effets secondaires. J'essaie de simuler le moins possible ( [ ]

  1. Enregistrez comment il a été appelé pour obtenir des données
  2. Renvoyer les données en conserve Enregistrer comment c'était
  3. Enregistrez comment il a été appelé pour insérer des données manipulées.

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X