La valeur des tests unitaires de haut niveau et des objets fictifs [fermé]

Je commence à croire que les tests unitaires de haut niveau, le code bien écrit, qui nécessite une utilisation intensive d'objets fictifs, a peu ou pas de valeur. Je me demande si cette affirmation est correcte, ou est-ce que je manque quelque chose?

Qu'est-ce que je veux dire par haut niveau?{[4] } ce sont les classes et les fonctions proches du sommet de la chaîne alimentaire. Leur entrée et leur sortie ont tendance à être l'entrée de l'utilisateur et l'interface utilisateur. La plupart de leur travail consiste à prendre l'entrée de l'utilisateur et à faire une série d'appels à entités de niveau inférieur. Ils ont souvent peu ou pas de valeurs de retour significatives.

Qu'est-ce que je veux dire par bien écrit? dans ce cas, je fais référence à un code découplé de ses dépendances (en utilisant des interfaces et une injection de dépendance), et ligne par ligne est à un niveau d'abstraction cohérent. Il n'y a pas d'algorithmes difficiles,et peu de conditions.

Je déteste écrire des tests unitaires pour ce type de code. Les tests unitaires consistent presque entièrement en une configuration d'objet fictif. 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 fictive est appelée, puis j'affirme que cette méthode fictive est appelée...", etc. je devrais tester le comportement de la méthode, pas qu'elle appelle la bonne séquence de méthodes . Autre chose: j'ai trouvé que ces tests sont extrêmement fragiles au refactoring . Si un test est si fragile il se brise complètement et doit être réécrit lorsque le code testé est refactorisé, l'un des principaux avantages des tests unitaires n'a-t-il pas été perdu?

Je ne veux pas que ce post soit signalé comme argumentatif, ou pas une question. Donc, je vais énoncer ma question directement: Quelle est la bonne façon de tester unitaire le type de code que j'ai décrit, ou est-il compris que Tout n'a pas besoin d'un test unitaire?

60
demandé sur Landon Kuhn 2009-07-07 09:33:48

5 réponses

Dans mon expérience, le niveau inférieur de votre code est (court d'être trivial), plus les tests unitaires de valeur sont, par rapport à l'effort requis pour les écrire. À mesure que vous montez plus haut dans la chaîne alimentaire, les tests deviennent de plus en plus élaborés et plus coûteux.

Les tests unitaires sont critiques car ils vous disent quand vous cassez quelque chose pendant le refactoring.

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 des tests d'acceptation. Les tests d'intégration sont nécessaires car ils vous indiquent à quel point les différents composants logiciels fonctionnent ensemble.

Les tests D'acceptation sont ce que le client signe. 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.

La moquerie n'est utile que pour les tests unitaires. Pour l'intégration et l'acceptation tests, mocking est inutile car il n'exerce pas les composants du système réels, tels que la base de données et l'infrastructure de communication.

43
répondu Robert Harvey 2009-07-07 06:02:02

Un Côté

Juste pour toucher à votre déclaration en gras:

" je devrais tester le comportement de la méthode, non pas que c'est l'appel de la bonne séquence de méthodes"

Le comportement de l'objet sous test est la séquence d'actions qu'il faut. C'est en fait un test de "comportement", alors que lorsque vous dites "comportement de la méthode", je pense que vous voulez dire un test avec État, comme dans, donnez-lui une entrée et vérifiez la sortie correcte.

Je fais ça distinction parce que certains BDD puristes vont jusqu'à dire que c'est beaucoup plus significatif pour tester ce que votre classe devrait appeler, plutôt que ce que les entrées et les sorties sont, parce que si vous savez comment votre système fonctionne, alors vos entrées et vos sorties seront correctes.

Une réponse

Cela mis à part, personnellement, je n'écris jamais de tests complets pour la couche D'interface utilisateur. Si vous utilisez un modèle MVVM, MVP ou MVC pour votre application, alors au niveau" 1-developer team", c'est hallucinant et contre-productif pour moi de le faire. Je peux Voir les bugs dans l'interface utilisateur, et oui, les comportements moqueurs à ce niveau ont tendance à être fragiles. Je suis beaucoup plus préoccupé par m'assurer que mes couches de domaine et DAL sous-jacentes fonctionnent correctement.

Ce est de valeur au plus haut niveau est un test d'intégration. Vous avez une application web? Au lieu d'affirmer que vos méthodes de contrôleur renvoient un ActionResult (test de faible valeur), écrivez un test d'intégration cela demande toutes les pages de votre application et s'assure qu'il n'y a pas de 404 ou de 403. exécutez-le une fois sur chaque déploiement.

Réponse Finale

je suis toujours la règle 80/20 avec des tests unitaires. Pour obtenir cette dernière couverture de 20% au niveau élevé dont vous parlez, va être 80% de votre effort. Pour mes projets personnels et la plupart de mes projets de travail, cela ne paie pas.

Bref, je suis d'accord. Je voudrais écrire des tests d'intégration, et ignorer les tests unitaires pour le code que vous décrivez.

20
répondu womp 2009-07-07 06:02:42

Je pense que cela dépend fortement de l'environnement. Si vous faites partie d'une équipe relativement petite et que vous pouvez maintenir l'intégrité des tests, les parties les plus complexes de votre application doivent comporter des tests unitaires. Selon mon expérience, le maintien de l'intégrité des tests sur les grandes équipes est assez difficile, car les tests sont initialement ok jusqu'à ce qu'ils soient inévitablement break...at quel point ils sont soit a) "fixés" d'une manière qui nie complètement leur utilité, ou b) rapidement commenté.

Le point principal de Les tests simulés semblent être de sorte que les gestionnaires peuvent prétendre que la métrique de couverture de code est à Foo%....so tout doit fonctionner! Le seul cas exceptionnel où ils sont éventuellement utiles est lorsque vous avez besoin de tester une classe qui est une énorme douleur à recréer authentiquement(tester une classe D'Action dans Struts, par exemple).

Je crois beaucoup à l'écriture de tests bruts. Code réel, avec des objets réels. Le code à l'intérieur des méthodes changera au fil du temps, mais le but et donc le comportement global habituellement ne le fait pas.

3
répondu meiguoren 2009-07-07 06:32:16

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

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

0
répondu Robert Koritnik 2009-07-07 06:04:05

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

Au niveau de test d'intégration (et/ou de système) supérieur, j'entre dans le test des choses qui ont des effets secondaires. J'essaie de fantaisie que peu que possible (éventuellement seulement des ressources externes) à ce stade. Un exemple serait de moquer la couche de base de données à:

  1. Enregistrer comment il a été appelé pour obtenir des données
  2. retourner l'enregistrement de données en conserve comment c'était
  3. enregistrez comment il a été appelé pour insérer des données manipulées
0
répondu RHSeeger 2009-07-07 13:49:31