Est-il raisonnable de ne pas écrire de tests unitaires parce qu'ils ont tendance à être commentés plus tard ou parce que les tests d'intégration ont plus de valeur?

35

Je discutais des tests unitaires / d'intégration avec un collègue et ce dernier a présenté un argument intéressant contre l' écriture de tests unitaires. Je suis un grand partisan des tests unitaires (principalement JUnit), mais je suis intéressé par les prises de vues des autres, car il a soulevé des points intéressants.

Pour résumer ses points:

  • En cas de modifications majeures du code (nouvel ensemble de POJO, refactoring majeur d’applications, etc.), les tests unitaires ont tendance à faire l’objet d’un commentaire plutôt qu’à être retravaillés.
  • Le temps est mieux dépensé sur les tests d'intégration couvrant les cas d'utilisation, ce qui rend les tests de moindre envergure moins importants / pas du tout importants.

Des pensées à ce sujet? Je suis toujours pro-unit test (car je vois constamment qu'il produit un code amélioré), bien que les tests d'intégration semblent au moins aussi précieux.

Jeff Levine
la source
9
Vous l'avez dit vous-même: l'écriture de tests unitaires produit un meilleur code car elle vous oblige à écrire du code testable. Le code testable par unité a beaucoup de propriétés très importantes qui améliorent sa qualité globale.
Robert Harvey
1
@ Robert Harvey - D'accord - Je serais intéressé de voir une liste de ces propriétés.
Jeff Levine
18
En ce qui concerne le premier point, vous dites essentiellement que l’un des inconvénients des tests unitaires est qu’ils ne fonctionnent pas lorsque vous ne les utilisez pas. Vous pouvez dire la même chose de toute autre pratique. En outre, votre utilisation de la voix passive dissimule le fait que quelqu'un commente activement les tests unitaires. Il semble donc que les développeurs de l’équipe n’adhèrent pas au projet, ce qui pose un problème différent.
JacquesB

Réponses:

62

J'ai tendance à me ranger du côté de votre ami parce que trop souvent, les tests unitaires testent les mauvaises choses .

Les tests unitaires ne sont pas mauvais en soi. Mais ils testent souvent les détails de la mise en œuvre plutôt que le flux d'entrées / sorties. Vous vous retrouvez avec des tests complètement inutiles lorsque cela se produit. Ma règle personnelle est qu'un bon test unitaire vous dit que vous venez de casser quelque chose; un mauvais test unitaire vous indique simplement que vous venez de changer quelque chose.

Un exemple qui me vient à l’esprit est un test qui a été intégré à WordPress il ya quelques années. La fonctionnalité testée tournait autour de filtres qui s’appelaient, et les tests vérifiaient que les rappels seraient ensuite appelés dans le bon ordre. Mais au lieu de (a) exécuter la chaîne pour vérifier que les rappels sont appelés dans l'ordre attendu, les tests se sont concentrés sur (b) la lecture d'un état interne qui n'aurait probablement pas dû être exposé pour commencer. Changer les éléments internes et (b) devient rouge; alors que (a) ne devient rouge que si des modifications apportées aux éléments internes annulent le résultat attendu en le faisant. (b) était clairement un test inutile à mon avis.

Si vous avez une classe qui expose quelques méthodes au monde extérieur, la chose correcte à tester à mon avis sont les dernières méthodes uniquement . Si vous testez également la logique interne, vous pouvez exposer la logique interne au monde extérieur, en utilisant des méthodes de test compliquées ou en vous lançant dans une litanie de tests unitaires qui se cassent invariablement lorsque vous souhaitez modifier quoi que ce soit.

Cela dit, je serais surpris que votre ami soit aussi critique à propos des tests unitaires que ce que vous semblez suggérer. Je pense plutôt qu'il est pragmatique. C'est-à-dire qu'il a observé que les tests unitaires écrits sont généralement inutiles. Citant: "les tests unitaires ont tendance à être commentés plutôt que retravaillés". Pour moi, il y a un message implicite là-dedans - s'ils ont tendance à avoir besoin de retravailler, c'est parce qu'ils ont tendance à être nul. En supposant que tel soit le cas, la deuxième proposition est la suivante: les développeurs perdraient moins de temps à écrire du code plus difficile à interpréter, à savoir des tests d’intégration.

En tant que tel, il ne s'agit pas d'un être meilleur ou pire. C'est juste qu'il est beaucoup plus facile de se tromper, et même très souvent mal en pratique.

Denis de Bernardy
la source
13
Ça, ça, cent fois ça. C'est une bonne chose pour le développement piloté par les tests. Écrire des tests d’abord vous aidera à écrire des tests qui ne testent pas les détails de votre implémentation, mais le comportement voulu que vous tentiez d’implémenter.
wirrbel
9
Je pense que les tests unitaires fragiles ne sont pas un trait inhérent aux tests unitaires, mais au lieu de mal comprendre ce que sont les tests unitaires. Ce dont nous avons besoin ici, c'est d'une meilleure éducation pour les développeurs. Si vous testez les détails d'implémentation des tests unitaires, vous le faites mal . Si vous rendez les méthodes privées publiques "afin de les tester", vous le faites mal . Testez toujours l'interface publique et le comportement, ce qui devrait changer moins souvent que les détails d'implémentation!
Andres F.
2
@ DocBrown: En effet non. Ce que je voulais dire, c’est que si souvent que le collègue d’OP a tort, c’est si souvent mal fait - ou du moins dans tous les cas que j’ai rencontré dans des entreprises obsédées par les tests unitaires.
Denis de Bernardy
3
@DenisdeBernardy: mon point est le suivant: tout ce que vous avez écrit est sûrement correct, avec beaucoup de sagesse de la pratique, mais je manque une conclusion quant à ce que cela signifie pour le cas des PO avec son collègue, dans le contexte de la suggestion de "tests d'intégration meilleure alternative ".
Doc Brown
5
Je suis fortement d'accord avec Doc Brown. En gros, votre position semble être que les tests unitaires sont bons, mais quand ils sont mal écrits, ils deviennent embêtants. Ainsi, vous n'êtes pas du tout d'accord avec le collègue d'OP, mais simplement de vous assurer que vous écrivez réellement des tests unitaires avant d'affirmer leur utilité. Je pense que votre réponse est très déroutante puisque la première phrase dit que vous êtes d’accord avec le collègue d’OP alors que cela ne semble pas être le cas. Cela ressemble plus à un discours sur les tests unitaires mal écrits (ce qui est un problème en pratique, je le concède), et non à l’affaire des tests unitaires en général.
Vincent Savard
38

Lorsque des modifications majeures du code se produisent, les tests unitaires ont tendance à être commentés plutôt que retravaillés.

Avec un groupe de codeurs cow-boys indisciplinés qui pensent que tous les tests deviennent "verts" est rempli lorsque vous commentez tous les tests existants: bien sûr. Le remaniement des tests unitaires requiert la même discipline que la rédaction de première main. Vous devez donc travailler ici pour définir la "définition du fait" de votre équipe.

Le temps est mieux dépensé sur les tests d'intégration couvrant les cas d'utilisation, ce qui rend les tests de moindre envergure moins importants / pas du tout importants.

Ce n'est ni complètement faux ni complètement vrai. L’effort nécessaire pour rédiger des tests d’intégration utiles dépend beaucoup du type de logiciel que vous écrivez. Si vous avez un logiciel dans lequel les tests d'intégration peuvent être écrits aussi facilement que les tests unitaires, ils fonctionnent encore rapidement et vous offrent une bonne couverture de votre code, alors votre collègue marque un point. Mais pour de nombreux systèmes, j'ai constaté que ces trois points ne peuvent pas être facilement satisfaits par les tests d'intégration, c'est pourquoi vous devez vous efforcer de disposer également de tests unitaires.

Doc Brown
la source
C’est ce que j’ai ressenti pendant que nous en discutions. En supposant que des tests d'intégration complets et pertinents puissent être écrits pour un cas d'utilisation, il semble que le test unitaire serait un point discutable. (Bien que maintenant que j'écris ceci, je pense à des cas futurs inattendus - par exemple, notre serveur est transformé en service Web pour une autre application).
Jeff Levine
+1 Pour "vous devez travailler sur votre définition de fait". Bien dit!
Andres F.
14

Je ne sais pas si j'accepte les arguments de votre collègue.

  1. Si les tests unitaires sont commentés, c'est parce que quelqu'un est paresseux. Ce n'est pas la faute des tests unitaires. Et si vous commencez à utiliser la paresse comme excuse, vous pouvez vous opposer à presque toutes les pratiques qui nécessitent un petit effort.
  2. Si vous le faites bien, les tests unitaires ne doivent pas prendre beaucoup de temps. Ils ne vous empêchent pas de faire un travail plus important. Si nous convenons tous que la correction du code erroné est plus importante que toute autre chose, un test unitaire est sacrément important. C'est un moyen facile de tester les conditions de post. Sans cela, comment savez-vous que vous avez respecté les garanties de condition de poste? Et si vous ne l'avez pas fait, votre code est cassé.

Il existe d'autres arguments plus convaincants contre les tests unitaires. Eh bien, pas exactement contre eux. Commencez par les dommages de conception induits par le test DHD . C'est un écrivain amusant, alors lis-le. Ce que j'en ai tiré:

Si vous apportez des modifications importantes à la conception de vos tests unitaires, elles peuvent entraver la réalisation d'autres objectifs de votre projet. Si possible, demandez à une personne impartiale quelle approche est la plus facile à suivre. Si votre code hautement testable est difficile à comprendre, vous risquez de perdre tous les avantages que vous avez obtenus avec le test unitaire. La surcharge cognitive liée à une trop grande abstraction vous ralentit et facilite les erreurs de fuite. Ne le négligez pas.

Si vous voulez être nuancé et philosophique, il existe de nombreuses ressources:

Scant Roger
la source
+1 pour cet article, pourrait être une meilleure réponse au cas des PO que tout ce que nous pouvons écrire ici-
Doc Brown
+1 pour le point numéro 1 sur la paresse étant une excuse. Je ne saurais trop insister à quel point c'est frustrant. J'ai été là. Récemment, en fait.
Greg Burghardt
3
La raison 1 n'est pas la faute des tests unitaires, mais c'est toujours une raison contre les tests unitaires.
user253751
Après avoir lu l'article de DHH, ne manquez pas de regarder les vidéos dans lesquelles il discute de ce sujet avec Kent Beck et Martin Fowler (ils sont sur youtube) - de nombreuses personnes semblent avoir une version plus extrême de son propos que prévu, et il l'affine un peu dans ces discussions.
Jules
6

En cas de modifications majeures du code (nouvel ensemble de POJO, refactoring majeur d’applications, etc.), les tests unitaires ont tendance à faire l’objet d’un commentaire plutôt qu’à être retravaillés.

Ne fais pas ça. Si vous trouvez quelqu'un qui fait cela, tuez-le et veillez à ce qu'il ne se reproduise pas, car leurs enfants pourraient hériter de ce gène défectueux. Si je regarde les émissions de télévision de CSI, la clé est de disperser partout des échantillons d’ADN trompeurs. De cette façon, vous polluez la scène du crime avec tant de bruit que, vu la nature coûteuse et longue des tests ADN, la probabilité qu'ils vous le détectent est astronomique.


la source
4

les tests unitaires ont tendance à être commentés plutôt que retravaillés.

À ce stade, le processus de révision de code indique "corrige les tests unitaires" et le problème ne se pose plus :-) Si votre processus de révision de code ne détecte pas ce type de faille majeure dans le code soumis, c'est le problème.

Philip Kendall
la source
3

En cas de modifications majeures du code (nouvel ensemble de POJO, refactoring majeur d’applications, etc.), les tests unitaires ont tendance à faire l’objet d’un commentaire plutôt qu’à être retravaillés.

J'essaie toujours de garder le refactoring et le changement de fonctionnalité séparés. Lorsque j'ai besoin de faire les deux, je commets généralement le refactoring en premier.

Lors du refactoring de code sans changer de fonctionnalité, les tests unitaires existants sont supposés aider à garantir que le refactoring ne rompt pas accidentellement la fonctionnalité. Donc, pour un tel commit, je considérerais que la désactivation ou la suppression des tests unitaires est un signe d’avertissement majeur. Tous les développeurs qui le font devraient être avisés de ne pas le faire lors de la révision du code.

Il est possible que des modifications qui ne modifient pas les fonctionnalités entraînent toujours l'échec des tests unitaires en raison de tests unitaires erronés. Si vous comprenez le code que vous modifiez, la cause de ces échecs de tests unitaires est généralement évidente et facile à corriger.

Par exemple, si une fonction prend trois arguments, un test unitaire couvrant l'interaction entre les deux premiers arguments de la fonction n'aurait peut-être pas pris soin de fournir une valeur valide pour le troisième argument. Cette faille dans le test unitaire peut être révélée par une refactorisation du code testé, mais elle est facile à corriger si vous comprenez ce que le code est censé faire et ce que le test unitaire teste.

Lors du changement de fonctionnalités existantes, il sera généralement nécessaire de modifier également certains tests unitaires. Dans ce cas, les tests unitaires permettent de s’assurer que votre code modifie la fonctionnalité comme prévu et n’a pas d’effets secondaires indésirables.

Lorsque vous corrigez des bogues ou ajoutez de nouvelles fonctionnalités, vous devez généralement ajouter plusieurs tests unitaires. Pour ceux-ci, il peut être utile de valider d’abord les tests unitaires, puis le correctif ou la nouvelle fonctionnalité plus tard. Cela permet de vérifier plus facilement que les nouveaux tests unitaires n’ont pas réussi avec l’ancien code, mais qu’ils ont réussi avec le nouveau code. Cette approche n’est cependant pas entièrement dépourvue d’inconvénients, de sorte qu’il existe également des arguments en faveur de la validation simultanée de nouveaux tests unitaires et de mises à jour du code.

Le temps est mieux dépensé sur les tests d'intégration couvrant les cas d'utilisation, ce qui rend les tests de moindre envergure moins importants / pas du tout importants.

Il y a un élément de vérité à cela. Si vous pouvez obtenir une couverture des couches inférieures de la pile de logiciels avec des tests ciblant les couches les plus hautes de la pile de logiciels, vos tests peuvent être plus utiles lors du refactoring de code.

Je ne pense pas que vous trouverez un accord sur la distinction exacte entre un test unitaire et un test d'intégration. Et je ne m'inquiéterais pas si vous aviez un scénario de test dans lequel un développeur appelle un test unitaire et un autre, un test d'intégration, dans la mesure où ils peuvent convenir qu'il s'agit d'un scénario de test utile.

Kasperd
la source
3

Des tests d'intégration sont effectués pour vérifier si le système se comporte comme prévu, ce qui a toujours une valeur métier. Les tests unitaires ne font pas toujours cela. Les tests unitaires pourraient être des tests de code mort, par exemple.

Selon une philosophie de test, tout test qui ne vous fournit pas d'informations est un gaspillage. Lorsqu'un élément de code n'est pas en cours d'élaboration, ses tests unitaires sont toujours (ou presque) verts, ce qui vous donne peu ou pas d'informations.

Chaque méthode de test sera incomplète. Même si vous bénéficiez d'une couverture à 100% avec certaines métriques, vous ne passez toujours pas par presque tous les ensembles de données d'entrée possibles. Alors, ne pensez pas que les tests unitaires sont magiques.

J'ai souvent vu l'affirmation selon laquelle les tests unitaires améliorent votre code. À mon avis, les améliorations apportées par les tests unitaires au code consistent à forcer les personnes qui n'y sont pas habituées à diviser leur code en petits morceaux bien factorisés. Si vous avez l'habitude de concevoir votre code en petites parties bien pondérées, vous constaterez peut-être que vous n'avez plus besoin de tests unitaires pour vous forcer à le faire.

En fonction du nombre de tests unitaires et de la taille de votre programme, vous pouvez vous retrouver avec une quantité énorme de tests unitaires. Si c'est le cas, les gros changements deviennent plus difficiles. Si un grand changement entraîne de nombreux tests unitaires échoués, vous pouvez refuser d'effectuer le changement, effectuer beaucoup de travail pour corriger de nombreux tests ou rejeter les tests. Aucun des choix n'est idéal.

Les tests unitaires sont un outil de la boîte à outils. Ils sont là pour vous servir, pas l'inverse. Assurez-vous d'en sortir au moins autant que vous en avez mis.

Michael Shaw
la source
3

Les tests unitaires ne sont certainement pas la solution miracle que certains promoteurs prétendent être. Mais en général, leur rapport coûts / avantages est très positif. Ils ne rendront pas votre code "meilleur", peut-être plus modulaire. "mieux" a tellement plus de facteurs que ce que "testabilité" implique. Mais ils veilleront à ce que cela fonctionne dans tous les cas et usages auxquels vous avez pensé et éliminera la plupart de vos bogues (probablement les plus triviaux).

Le principal avantage des tests unitaires est qu'ils rendent votre code plus flexible et résilient au changement. Vous pouvez refactoriser ou ajouter des fonctionnalités et être assez confiant de ne rien casser. Cela seul les rend rentables pour la plupart des projets.

En référence aux points soulevés par votre ami:

  1. Si l'ajout de POJO casse vos tests unitaires, ils sont probablement très mal écrits. Les POJO sont généralement la langue que vous utilisez pour parler de votre domaine. Les problèmes que vous résolvez, leur modification signifie évidemment toute votre logique métier et le code de présentation doit probablement changer. Vous ne pouvez pas vous attendre à ce que ce qui assure que ces parties de votre programme ne changent pas ...

  2. Se plaindre que les tests unitaires sont mauvais, parce que les gens les commentent, c'est comme se plaindre d'une ceinture de sécurité, parce que les gens ne les portent pas. Si quelqu'un commente un code de test et casse quelque chose, il devrait se retrouver dans une conversation très sérieuse et désagréable avec son patron ...

  3. Les tests d'intégration sont de loin supérieurs aux tests unitaires. pas de question. surtout si vous testez un produit. Mais écrire de bons tests d'intégration complets, qui vous donneront la même valeur, la même couverture et la même exactitude que les tests unitaires, est incroyablement difficile.

AK_
la source
2

Je ne peux penser qu'à un seul argument contre les tests unitaires, et il est assez faible.

Les tests unitaires montrent la majorité de leur valeur lorsque vous modifiez ou modifiez le code ultérieurement. Vous constatez une réduction considérable du temps requis pour ajouter des fonctionnalités et vous assurer que vous n'avez rien cassé.

Cependant: Il y a un coût (petit) en temps d'écriture des tests en premier lieu cependant.

Par conséquent: si un projet est suffisamment court et ne nécessite pas de maintenance / mises à jour régulières, il est possible que le coût de la rédaction des tests soit supérieur à leurs avantages.

Ewan
la source
+1 pour avoir tenté de répondre sérieusement à la question posée.
RubberDuck
2
Le coût du test unitaire est définitivement peu élevé. Les bons tests unitaires prennent généralement plus de temps à écrire que ce qu'ils testent. La plupart du temps, les avantages dépassent les coûts.
AK_
1
en théorie seulement - quand vous refactorisez, vous vous retrouvez avec un coût énorme de mise à jour des tests unitaires, car ils sont presque toujours trop granulaires. Si vous parliez d'une bonne suite de tests d'intégration, vous auriez alors raison.
gbjbaanb
Si un projet est suffisamment court et ne nécessite pas de maintenance / mises à jour continues - la plupart des systèmes existants ont commencé comme un projet court sans test - et finissent par engager plus de développeurs pour maintenir le système développé sans test
Fabio le