web-dev-qa-db-fra.com

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 sont plus utiles?

Je discutais des tests unitaires/d'intégration avec un collègue, et il a fait un cas intéressant contre en écrivant des tests unitaires. Je suis un grand partisan des tests unitaires (JUnit principalement), mais je suis intéressé à entendre les opinions des autres, car il a fait des remarques intéressantes.

Pour résumer ses points:

  • Lorsque des modifications majeures de code se produisent (nouvel ensemble de POJO, refactorisation d'application majeure, etc.), les tests unitaires ont tendance à être commentés plutôt que retravaillés.
  • Il est préférable de passer du temps sur les tests d'intégration couvrant les cas d'utilisation, ce qui rend les tests de plus petite envergure moins/pas du tout importants.

Réflexions là-dessus? Je suis toujours un test pro-unit (comme je le vois constamment produire du code amélioré), bien que les tests d'intégration semblent au moins aussi précieux.

35
Jeff Levine

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 intrinsèquement mauvais. Mais ils testent souvent les détails d'implémentation plutôt que le flux d'entrée/sortie. Vous vous retrouvez avec des tests complètement inutiles lorsque cela se produit. Ma propre règle est que n bon test unitaire vous dit que vous venez de casser quelque chose; un mauvais test unitaire vous dit simplement que vous venez de changer quelque chose.

Un exemple sur le dessus de ma tête est un test qui a été glissé dans WordPress il y a quelques années. La fonctionnalité testée tournait autour de filtres qui s'appelaient les uns les autres, et les tests vérifiaient que les rappels seraient 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 sans doute pas dû être exposé pour commencer . Changer les internes et (b) devient rouge; tandis que (a) ne devient rouge que si les modifications apportées aux internes cassent le résultat escompté. (B) était clairement un test inutile à mon avis.

Si vous avez une classe qui expose quelques méthodes au monde extérieur, la bonne chose à tester à mon avis sont les dernières méthodes seulement . Si vous testez également la logique interne, vous pouvez finir par exposer la logique interne au monde extérieur, en utilisant des méthodes de test compliquées ou avec une litanie de tests unitaires qui se cassent invariablement chaque fois que vous voulez changer quoi que ce soit.

Cela dit, je serais surpris si votre ami est aussi critique à l'égard des tests unitaires en soi que vous semblez le suggérer. Je suppose plutôt qu'il est pragmatique. Autrement dit, il a observé que les tests unitaires qui sont écrits sont pour la plupart inutiles dans la pratique. Citant: "les tests unitaires ont tendance à être commentés plutôt qu'à être 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 à aspirer. En supposant que oui, la deuxième proposition suit: les développeurs perdraient moins de temps à écrire du code qui est plus difficile à se tromper - c'est-à-dire les tests d'intégration.

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

62
Denis de Bernardy

Lorsque des changements de code majeurs se produisent, les tests unitaires ont tendance à être commentés plutôt qu'à être retravaillés.

Avec un groupe indiscipliné de codeurs de cow-boy qui pensent que tous les tests deviennent "verts" lorsque vous commentez tous les tests existants: bien sûr. Retravailler les tests unitaires prend la même quantité de discipline que les écrire de première main, donc ce que vous devez travailler ici est la "définition du fait" de votre équipe.

Il est préférable de consacrer du temps aux tests d'intégration couvrant les cas d'utilisation, ce qui rend les tests de moindre envergure moins importants, voire pas du tout importants.

Ce n'est ni complètement faux ni tout à fait raison. L'effort pour écrire des tests d'intégration utiles dépend beaucoup du type de logiciel que vous écrivez. Si vous avez un logiciel où les tests d'intégration peuvent presque aussi facilement être écrits que les tests unitaires, ils fonctionnent toujours rapidement et vous donnent une bonne couverture de votre code, alors votre collègue a raison. Mais pour beaucoup de systèmes, j'ai vu que ces trois points ne peuvent pas être facilement remplis par des tests d'intégration, c'est pourquoi vous devez vous efforcer d'avoir également des tests unitaires.

38
Doc Brown

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 paresseux comme excuse, vous pouvez vous opposer à presque toutes les pratiques qui nécessitent un peu d'effort.
  2. Si vous le faites correctement, 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 réparation du code cassé est plus importante que toute autre chose, un test unitaire est sacrément important. C'est un moyen facile de tester les conditions de publication. Sans cela, comment savez-vous que vous avez respecté les garanties de condition de poste? Et si ce n'est pas le cas, votre code est cassé.

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

Si vous apportez des modifications de conception importantes pour tenir compte de vos tests unitaires, elles peuvent entraver 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 retirés des tests unitaires. La surcharge cognitive de trop d'abstraction vous ralentit et facilite les erreurs de fuite. Ne l'escomptez pas.

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

13
Scant Roger

Lorsque des modifications majeures de code se produisent (nouvel ensemble de POJO, refactorisation d'application majeure, etc.), les tests unitaires ont tendance à être commentés plutôt que retravaillés.

Ne fais pas ça. Si vous trouvez quelqu'un qui fait cela, assassinez-le et assurez-vous qu'il ne se reproduit pas, car ses enfants pourraient hériter de ce gène défectueux. La clé que je vois en regardant les émissions de télévision de CSI est de disperser beaucoup d'échantillons d'ADN trompeurs partout. De cette façon, vous polluez la scène du crime avec tellement de bruit que, compte tenu de la nature coûteuse et chronophage des tests ADN, la probabilité qu'ils vous les identifient est astronomiquement faible.

6
user204677

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

À ce stade, le processus de révision du code dit "allez réparer les tests unitaires" et ce n'est plus un problème :-) Si votre processus de révision du code ne détecte pas ce type de faille majeure dans le code soumis, alors c'est le problème.

4
Philip Kendall

Lorsque des changements majeurs de code se produisent (nouvel ensemble de POJO, refactorisation d'application majeure, etc.), les tests unitaires ont tendance à être commentés plutôt qu'à être retravaillés.

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

Lors de la refactorisation du code sans modification des fonctionnalités, les tests unitaires existants sont censés garantir que la refactorisation ne casse pas accidentellement les fonctionnalités. Donc, pour un tel commit, je considérerais la désactivation ou la suppression des tests unitaires comme un signe d'avertissement majeur. Tout développeur qui le fait doit être informé de ne pas le faire lors de la révision du code.

Il est possible que des modifications qui ne modifient pas la fonctionnalité entraînent toujours l'échec des tests unitaires en raison de tests unitaires défectueux. Si vous comprenez le code que vous modifiez, la cause de ces échecs de test unitaire est généralement immédiatement évidente et facile à résoudre.

Par exemple, si une fonction prend trois arguments, un test unitaire couvrant l'interaction entre les deux premiers arguments de la fonction peut ne pas avoir 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 est facile à corriger si vous comprenez ce que le code est censé faire et ce que le test unitaire teste.

Lors de la modification d'une fonctionnalité existante, il sera généralement nécessaire de modifier également certains tests unitaires. Dans ce cas, les tests unitaires permettent de garantir que votre code modifie la fonctionnalité comme prévu et n'a pas d'effets secondaires involontaires.

Lors de la correction de bugs ou de l'ajout de nouvelles fonctionnalités, il faut généralement ajouter plus de tests unitaires. Pour ceux-ci, il peut être utile de valider les tests unitaires en premier et de valider le correctif de bogue ou la nouvelle fonctionnalité plus tard. Cela permet de vérifier plus facilement que les nouveaux tests unitaires ne sont pas passés avec l'ancien code mais passent avec le nouveau code. Cette approche n'est cependant pas entièrement sans inconvénients, il existe donc également des arguments en faveur de la validation simultanée de nouveaux tests unitaires et de mises à jour de code.

Il est préférable de passer du temps sur les tests d'intégration couvrant les cas d'utilisation, ce qui rend les tests de plus petite envergure moins/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 logicielle avec des tests ciblant les couches supérieures de la pile logicielle, vos tests peuvent être plus utiles lors de la refactorisation du 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 avez un cas de test qu'un développeur appelle un test unitaire et un autre appelle un test d'intégration, à condition qu'ils conviennent que c'est un cas de test utile.

3
kasperd

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

Il y a une philosophie de test qui dit que tout test qui ne vous donne pas d'informations est un gaspillage. Lorsqu'un morceau de code n'est pas en cours d'élaboration, ses tests unitaires sont toujours (ou presque toujours) verts, vous donnant peu ou pas d'informations.

Chaque méthode de test sera incomplète. Même si vous obtenez une couverture à 100% par une mesure, vous ne parcourez toujours pas presque tous les ensembles possibles de données d'entrée. Donc, ne pensez pas que les tests unitaires sont magiques.

J'ai souvent vu l'affirmation selon laquelle avoir des tests unitaires améliore votre code. À mon avis, les améliorations que les tests unitaires apportent au code obligent les gens qui n'y sont pas habitués à diviser leur code en petits morceaux bien factorisés. Si vous vous habituez normalement à concevoir votre code en petits morceaux bien factorisés, vous constaterez peut-être que vous n'avez plus besoin de tests unitaires pour vous forcer à le faire.

Selon l'ampleur de votre test unitaire et la taille de votre programme, vous pouvez vous retrouver avec une énorme quantité de tests unitaires. Si c'est le cas, les grands changements deviennent plus difficiles. Si un gros changement entraîne de nombreux tests unitaires échoués, vous pouvez refuser d'effectuer le changement, faire beaucoup de travail pour corriger un grand nombre de tests ou les jeter. 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 retirer au moins autant que vous en avez mis.

3
Michael Shaw

Les tests unitaires ne sont certainement pas la solution miracle que certains partisans prétendent qu'ils sont. Mais en général, ils ont un rapport coûts/avantages 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 assureront que cela fonctionne dans tous les cas et usages auxquels vous avez pensé et éliminera la plupart de vos bugs (probablement les plus triviaux).

Le plus grand 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 que vous n'avez rien cassé. Cela seul en vaut la peine pour la plupart des projets.

Se référant 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, et les problèmes que vous résolvez, les changer signifie évidemment votre entier logique métier, et probablement le code de présentation doit changer. Vous ne pouvez pas vous attendre à ce qui garantit que ces parties de votre programme ne changeront pas ...

  2. Se plaindre que les tests unitaires sont mauvais, parce que les gens les commentent, c'est comme se plaindre que les ceintures de sécurité sont mauvaises parce que les gens ne les portent pas. Si quelqu'un commente le 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 il est extrêmement difficile de rédiger de bons tests d'intégration complets, qui vous donneront la même valeur, la couverture et l'assurance d'exactitude que le test unitaire.

3
AK_

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

Les tests unitaires montrent la majorité de leur valeur lorsque vous refactorisez ou modifiez le code à un stade ultérieur. Vous voyez une énorme réduction du temps nécessaire pour ajouter des fonctionnalités et vous assurer que vous n'avez rien cassé.

Cependant: il y a un (petit) coût 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 continues, il est possible que le coût de l'écriture des tests l'emporte sur leurs avantages.

2
Ewan