Est-il nécessaire de conserver des tests pour des fonctions simples (autonomes)?

36

Considère ceci:

public function polynominal($a, $b, $c, $d)
{
    return  $a * pow($x, 3) + $b * pow($x, 2) + $c * $x + $d;
}

Supposons que vous écriviez divers tests pour la fonction ci-dessus et que vous prouviez à vous-même et aux autres que cela "fonctionne".

Pourquoi alors ne pas retirer ces tests et vivre heureux pour toujours? Mon argument est que certaines fonctions n'ont pas besoin d'être testées en permanence après que leur efficacité ait été prouvée. Je cherche des contre-points qui indiquent, oui, ces fonctions doivent encore être testées, car: ... ou que, oui, elles n'ont pas besoin d'être testées ...

Dennis
la source
32
N'est-il pas vrai pour chaque fonction que si vous ne modifiez pas votre code, si cela fonctionnait hier, cela fonctionnerait aussi demain? Le fait est que le logiciel est modifié.
5gon12eder
3
Parce que personne n'écrira jamais override_function('pow', '$a,$b', 'return $a * $b;');dans la raison ... ou n'essayera pas de le réécrire pour gérer des nombres complexes.
8
Alors ... euh ... ce "code prouvé sans bug" ... il a un bug .
Pour les petites fonctions comme celles-ci, vous pouvez envisager de tester les propriétés. Le test basé sur les propriétés génère automatiquement des cas de test et des tests sur des invariants prédéterminés. Ils fournissent une documentation à travers les invariants, ce qui les rend utiles à conserver. Pour des fonctions simples comme celle-ci, elles sont parfaites.
Martijn
6
Cette fonction est également un excellent candidat pour modifier le formulaire de Horner. Il (($a*$x + $b)*$x + $c)*$x + $dest facile de se tromper, mais est souvent beaucoup plus rapide. Ce n’est pas parce que vous pensez que cela ne changera pas.
Michael Anderson

Réponses:

78

Les tests de régression

Tout est question de test de régression .

Imaginez le prochain développeur qui examine votre méthode et remarque que vous utilisez des nombres magiques. On lui a dit que les nombres magiques sont diaboliques, il a donc créé deux constantes, une pour le numéro deux, l'autre pour le numéro trois - il n'y a rien de mal à faire ce changement; ce n'est pas comme s'il modifiait votre implémentation déjà correcte.

Etre distrait, il inverse deux constantes.

Il valide le code et tout semble bien fonctionner, car aucun test de régression n'est exécuté après chaque validation.

Un jour (peut-être des semaines plus tard), quelque chose se casse ailleurs. Et par ailleurs, je veux dire dans l'emplacement complètement opposé de la base de code, ce qui semble n'avoir rien à voir avec la polynominalfonction. Des heures de débogage douloureux mènent au coupable. Pendant ce temps, l'application continue d'échouer en production, causant de nombreux problèmes à vos clients.

Garder les tests originaux que vous avez écrits pourrait éviter une telle douleur. Le développeur distrait commettrait le code et verrait presque immédiatement qu'il avait cassé quelque chose; un tel code n'atteindra même pas la production. Les tests unitaires seront en outre très précis sur l'emplacement de l'erreur . Le résoudre ne serait pas difficile.

Un effet secondaire ...

En fait, la plupart des refactorisations sont fortement basées sur des tests de régression. Faites un petit changement. Tester. Si ça passe, tout va bien.

L'effet secondaire est que si vous n'avez pas de test, pratiquement toute refactorisation devient un risque énorme de casser le code. Étant donné le nombre de cas, il est déjà difficile d’expliquer à la direction que la refactorisation doit être effectuée. Il serait encore plus difficile de le faire après que vos précédentes tentatives de refactoring aient introduit plusieurs bogues.

En proposant une suite complète de tests, vous encouragez la refactorisation et, par conséquent, un code plus propre. Sans risque, il devient très tentant de refactoriser davantage, sur une base régulière.

Changements dans les exigences

Un autre aspect essentiel est que les exigences changent. Vous serez peut-être invité à gérer des nombres complexes et vous devrez soudainement effectuer une recherche dans votre journal de contrôle de version pour rechercher les tests précédents, les restaurer et commencer à ajouter de nouveaux tests.

Pourquoi tout ce tracas? Pourquoi supprimer des tests pour les ajouter plus tard? Vous auriez pu les garder en premier lieu.

Arseni Mourzenko
la source
Note pédante: rien n’est sans risque. ;)
jpmc26
2
La régression est la raison numéro un des tests pour moi - même si votre code définit clairement les responsabilités et n’utilise que ce qui lui est transmis (par exemple, aucun élément global), c’est trop facile de casser quelque chose par accident. Les tests ne sont pas parfaits, mais ils restent excellents (et empêchent presque toujours le même bogue de réapparaître, ce que la plupart des clients ne sont pas mécontents). Si vos tests fonctionnent, il n'y a aucun coût de maintenance. S'ils cessent de fonctionner, vous avez probablement trouvé un bogue. Je travaille sur une application héritée avec des milliers de globaux - sans tests, je n'oserais pas apporter beaucoup des changements nécessaires.
Luaan
Une autre remarque pédante: certains nombres "magiques" sont en fait corrects, et les remplacer par des constantes est une mauvaise idée.
Deduplicator
3
@Deduplicator, nous le savons, mais beaucoup de jeunes programmeurs récemment formés à l'université sont extrêmement zélés pour suivre aveuglément des mantras. Et "les nombres magiques sont diaboliques" en fait partie. Une autre est "tout ce qui est utilisé plus d'une fois doit être transformé en une méthode" conduisant dans des cas extrêmes à une pléthore de méthodes ne contenant qu'une seule déclaration (et se demandent ensuite pourquoi les performances ont soudainement chuté, elles n'ont jamais été informées des callstacks).
Jwenting
@jwenting: Je viens juste d'ajouter cette note car MainMa a écrit "il n'y a rien de mal à faire ce changement".
Déduplicateur
45

Parce que rien n'est si simple qu'il ne peut y avoir de bugs.

Votre code, à première vue, semble être exempt de bogues. Il s’agit en fait d’une simple représentation programmatique d’une fonction polynomiale.

Sauf qu'il a un bug ...

public function polynominal($a, $b, $c, $d)
{
    return  $a * pow($x, 3) + $b * pow($x, 2) + $c * $x + $d;
}

$xn'est pas défini comme une entrée dans votre code et, selon la langue, le runtime ou la portée, votre fonction peut ne pas fonctionner, entraîner des résultats non valides ou bloquer un vaisseau spatial .


Addenda:

Bien que vous considériez votre code sans bug pour le moment, il est difficile de dire combien de temps il reste. On pourrait faire valoir qu’écrire un test pour un élément de code aussi trivial n’en vaut pas la peine, mais le fait que le test ait déjà été écrit et son effacement supprime un dispositif de sécurité tangible.

Il est à noter que les services de couverture de code (tels que coveralls.io) donnent une bonne indication de la couverture fournie par une suite de tests. En couvrant chaque ligne de code, vous donnez une mesure décente de la quantité (sinon de la qualité) des tests que vous effectuez. Combinés à de nombreux petits tests, ces services vous indiquent au moins où ne pas rechercher un bogue lorsque cela se produit.

En fin de compte, si vous avez déjà un test écrit, conservez-le . Parce que le gain de temps ou d'espace résultant de sa suppression sera probablement bien inférieur à la dette technique ultérieurement si un bogue survient.


la source
Il existe une solution alternative: basculer vers des langages non dynamiques non faibles. Les tests unitaires sont généralement une solution de contournement pour la vérification de la compilation de temps suffisamment forte et certaines langues sont un peu à ce bon en.wikipedia.org/wiki/Agda_(programming_language)
Den
21

Oui. Si nous pouvions dire avec 100% de confiance, avec certitude: cette fonction ne sera jamais modifiée et ne fonctionnera jamais dans un contexte susceptible de l’échouer. Si nous pouvions dire cela, nous pourrions abandonner les tests et économiser quelques millisecondes à chaque fois. Construction de CI.

Mais nous ne pouvons pas. Ou, nous ne pouvons pas avec beaucoup de fonctions. Et il est plus simple d’avoir pour règle de faire tous les tests en permanence que de s’efforcer de déterminer exactement le seuil de confiance dont nous sommes satisfaits et le degré de confiance que nous avons dans l’immuabilité et l’infaillibilité d’une fonction donnée.

Et le temps de traitement est bon marché. Ces millisecondes économisées, même multipliées à de nombreuses reprises, ne totalisent pas assez pour justifier de prendre le temps avec chaque fonction pour demander: avons-nous suffisamment confiance en nous pour qu'il ne soit plus nécessaire de les tester à nouveau?

Carl Manaster
la source
Je pense que c'est un très bon point que vous soulevez ici. Je vois déjà deux gars passer des heures à se disputer pour garder des tests pour certaines petites fonctionnalités.
Kapol
@Kapol: pas un argument, une réunion pour déterminer ce qui peut aller et ce qui peut rester, et quels critères devraient être utilisés pour décider, ainsi que le lieu de documentation des critères, et qui
approuve la
12

Tout ce qui est dit dans les autres réponses est correct, mais je vais en ajouter une de plus.

Documentation

Les tests unitaires, s'ils sont bien écrits, peuvent expliquer aux développeurs ce que fait une fonction, quelles sont ses attentes en matière d'entrées / sorties et, plus important encore, quel comportement on peut en attendre.

Cela peut faciliter la détection d'un bogue et réduire la confusion.

Tout le monde ne se souvient pas des polynômes, de la géométrie ou même de l'algèbre :) Mais un bon test unitaire enregistré dans le contrôle de version s'en souviendra pour moi.

Pour un exemple d’utilité en tant que documentation, consultez l’introduction de Jasmine: http://jasmine.github.io/edge/introduction.html Donnez-lui quelques secondes à charger, puis faites défiler vers le bas. L'ensemble de l'API Jasmine sera documentée sous forme de sortie de test unitaire.

[Mise à jour basée sur les commentaires de @Warbo] Les tests sont également garantis, car s'ils échouent, ils échoueront, ce qui provoquera généralement un échec de construction si le CI est utilisé. La documentation externe change indépendamment du code et n'est donc pas nécessairement à jour.

Brandon
la source
1
L'utilisation de tests en tant que (une partie de) documentation que vous avez laissée implicite présente un gros avantage: ils sont automatiquement vérifiés. Autres formes de documentation, par exemple. des commentaires explicatifs ou des extraits de code sur une page Web peuvent devenir obsolètes à mesure que le code évolue. Les tests unitaires déclencheront un échec s'ils ne sont plus précis. Cette page de jasmin en est un exemple extrême.
Warbo
J'aimerais ajouter que certaines langues, telles que D et Rust, intègrent leur génération de documentation à leurs tests unitaires, de sorte que vous puissiez avoir le même fragment de code compilé dans un test unitaire et inséré dans la documentation HTML
Idan Arye
2

Vérification de la réalité

J'ai été dans des environnements difficiles où les tests sont "une perte de temps" lors de la budgétisation et du calendrier, puis "un élément fondamental de l'assurance qualité" une fois que le client a affaire à des bugs, mon avis est donc plus fluide que les autres.

Vous avez un budget. Votre travail consiste à obtenir le meilleur produit possible avec ce budget, quelle que soit la définition du terme "meilleur" que vous pouvez rassembler (ce n'est pas un mot facile à définir). Fin de l'histoire.

Le test est un outil dans votre inventaire. Vous devriez l’utiliser, car c’est un bon outil , qui économise des millions, voire des milliards de dollars depuis longtemps. Si vous en avez l'occasion, vous devriez ajouter des tests à ces fonctions simples. Cela peut sauver votre peau un jour.

Mais dans le monde réel, avec des contraintes de budget et de calendrier, cela peut ne pas arriver. Ne vous faites pas l'esclave de la procédure. Les fonctions de test sont utiles, mais à un moment donné, vos heures de travail seront peut-être mieux consacrées à la rédaction de la documentation pour développeurs en mots, plutôt qu'en code, afin que le développeur suivant n'ait pas besoin de autant de tests. Ou alors, il serait peut-être préférable de refactoriser la base de code pour ne pas avoir à maintenir une bête aussi difficile. Ou peut-être préféreriez-vous plutôt passer votre temps à discuter du budget et du calendrier avec votre supérieur hiérarchique afin qu'il comprenne mieux ce à quoi il aspirait lors de la prochaine ronde de financement.

Le développement de logiciels est un équilibre. Comptez toujours le coût d'opportunité de tout ce que vous faites pour vous assurer qu'il n'y a pas de meilleure façon de dépenser votre temps.

Cort Ammon - Rétablir Monica
la source
4
Dans ce cas, il y avait déjà un test, la question n'est donc pas de savoir s'il faut les écrire, mais s'il faut les valider et les exécuter avec chaque version ou version ou quel que soit le calendrier prévu pour l'exécution de tous les tests.
Paŭlo Ebermann
0

Oui, gardez les tests, maintenez-les en marche et maintenez-les en réussite

Les tests unitaires sont là pour vous protéger (et protéger les autres) de vous-même (et d'eux-mêmes).

Pourquoi garder les tests une bonne idée?

  • Valider la fonctionnalité des exigences précédentes face aux nouvelles exigences et fonctionnalités supplémentaires
  • Vérifier que les exercices de refactoring sont corrects
  • Documentation interne - voici comment le code devrait être utilisé
  • Tests de régression, les choses changent
    • Est-ce que les changements cassent l'ancien code?
    • Les modifications nécessitent-elles des demandes de modification supplémentaires ou des mises à jour des fonctions ou du code actuels?
  • Étant donné que les tests sont déjà écrits, conservez-les; c'est le temps et l'argent déjà dépensés qui réduiront les coûts de maintenance
Niall
la source
2
Cela ne semble rien offrir de substantiel par rapport aux autres réponses fournies.
1
Il y avait un problème pour l'afficher plus tôt, mais rétrospectivement, c'est un bon résumé. Je vais l'enlever.
Niall
-1

Documentation développeur

  • Comment puis-je (en tant qu'autre développeur) savoir que cela a été testé?
  • Si je veux corriger un bogue dans l' autonomie fonction , comment puis-je savoir que je n'introduis pas un bogue que vous aviez déjà envisagé?
  • Indicateur de complexité: Le nombre de tests peut être une bonne mesure de la complexité d'un élément. Cela peut indiquer que vous ne devez pas le toucher car il est mature et stable ou gonflé et couplé.

Documentation utilisateur

  • En attendant une mauvaise documentation, je peux regarder et voir si {zéro, les valeurs négatives, les ensembles vides, etc.} sont acceptés et quelle est la valeur de retour attendue.
  • Cela donne aussi un bon exemple de la façon dont je devrais utiliser l'objet / la fonction
soixantefootersdude
la source
1
cela ne semble rien offrir de substantiel par rapport aux points soulevés et expliqués dans les réponses précédentes. Même "un bon résumé" a déjà été posté (à mon goût, ce n'est pas si gentil mais bon)
mord