web-dev-qa-db-fra.com

Comment supprimer une fonction ou une fonctionnalité lors de l'utilisation de TDD

Dans les textes sur TDD, j'ai souvent lu sur "Supprimer la duplication" ou "Améliorer la lisibilité" lors de l'étape de refactorisation. Mais qu'est-ce qui me fait supprimer une fonction inutilisée?

Par exemple, disons qu'il y a une classe C avec des méthodes a() et b(). Maintenant, je pense que ce serait bien d'avoir une méthode f() qui est conduite dans C. En fait f() remplace tous les appels à b() à l'exception des tests d'unité définis/décrits b(). Il n'est plus nécessaire - sauf pour les tests.

Est-ce qu'il enregistre pour simplement supprimer b() et tous les tests qui l'utilisaient? Est-ce que cette partie de "améliorer la lisibilité"?

20
TobiMcNamobi

Oui bien sûr. Le code le plus facile à lire est celui qui n'est pas là.

Cela dit, le refactoring signifie généralement améliorer le code sans changer son comportement. Si vous pensez à quelque chose qui améliore le code, faites-le simplement. Il n'est pas nécessaire de l'adapter dans un trou de pigeon avant de pouvoir le faire.

16
Sebastian Redl

L'élimination d'une méthode publique n'est pas "refactoring" - refactoring modifie la mise en œuvre tout en continuant de transmettre des tests existants.

Cependant, l'élimination d'une méthode inutile est un changement de conception parfaitement raisonnable.

TDD l'établit dans une certaine mesure, car pour examiner les tests, vous pouvez observer que cela teste une méthode inutile. Les tests conduisent votre conception, car vous pouvez aller "regarder, ce test n'a rien à voir avec mon objectif".

Cela peut se révéler plus à des niveaux de test plus élevés, conjointement avec des outils de couverture de code. Si vous exécutez des tests d'intégration avec une couverture de code et que vous voyez que les méthodes ne sont pas appelées, il s'agit d'un indice qu'une méthode n'est pas utilisée. L'analyse de code statique peut également indiquer que les méthodes ne sont pas utilisées.

Il existe deux approches pour supprimer une méthode; Les deux travaillent dans différentes circonstances:

  1. Supprimer la méthode. Suivez les erreurs de compilation pour supprimer tout code et tests dépendants. Si vous êtes convaincu que les tests concernés sont jetables, engagez vos modifications. Sinon, lancez-vous.

  2. Supprimez les tests que vous jugez obsolètes. Exécutez toute votre suite de tests avec couverture de code. Supprimer des méthodes qui n'ont pas été exercées par la suite de tests.

(Cela présuppose que votre suite de test a une bonne couverture pour commencer).

27
slim

En fait, f() remplace tous les appels à b() à l'exception des tests d'unité définis/décrits B ()

IMHO Le cycle TDD typique ressemblera à ceci:

  • écrire des tests d'échec pour f() (probablement basé sur les tests de B ()): tests Go rouge

  • implémenter f() -> Les tests deviennent vert

  • Refactor: -> Supprimer b() et tous les tests de B ()

Pour la dernière étape, vous pourriez envisager de supprimer b() d'abord et voyez ce qui se passe (lors de l'utilisation d'une langue compilée, le compilateur doit se plaindre uniquement des tests existants, lorsqu'il n'est pas, l'ancienne unité teste pour B échouera, il est donc clair que vous devez les supprimer aussi).

10
Doc Brown

Oui c'est le cas.

Le code le plus lisible le mieux, la plupart des bugs, est le code qui n'existe pas. S'efforcer d'écrire autant de code que possible tout en respectant vos exigences.

4
Kilian Foth

Il est souhaitable de supprimer b() une fois qu'il n'est plus utilisé, pour la même raison qu'il est souhaitable de ne pas ajouter de fonctions non utilisées en premier lieu. Que vous appeliez la "lisibilité" ou quelque chose d'autre, tout le reste étant égal à ce que c'est une amélioration du code qu'il ne contient rien, il n'a aucune utilité. Pour avoir au moins une mesure spécifique par laquelle il vaut mieux ne pas l'avoir, la suppression de cela garantit que son coût d'entretien futur après ce changement est égal à zéro!

Je n'ai trouvé aucune technique spéciale pour être nécessaire pour le supprimer de ses tests, car toute pensée de remplacer b() avec quelque chose de nouveau doit bien sûr être accompagnée d'une prise en compte de tout code appelant actuellement b(), et les tests sont un sous-ensemble de "tout code".

La ligne de raisonnement qui fonctionne généralement pour moi est que, au point où je remarque que f() a fait b() obsolète, par conséquent b() devrait être au moins obsolète et je ' m Vous cherchez à trouver tous les appels vers b() avec l'intention de les remplacer par des appels à f(), Je considère également le code de test. Spécifiquement, si b() n'est plus nécessaire, je peux et je dois supprimer ses tests d'unité.

Vous êtes tout à fait correct que rien forces Moi de noter que b() n'est plus nécessaire. C'est une question de compétence (et, comme le dit mince, les rapports de couverture de codes sur des tests de niveau supérieur). Si seule une unité teste et qu'aucun test fonctionnel, reportez-vous à b(), puis je peux être prudemment optimiste qu'il ne fait pas partie d'une interface publiée et que la suppression de ce n'est pas un changement de rupture de tout code non sous mon contrôle direct. .

Le cycle rouge/vert/refacteur ne mentionne pas explicitement supprimer des tests. En outre, retirer b() viole le principe ouvert/fermé depuis clairement votre composant IS ouvert pour la modification. Donc, si vous voulez penser à cette étape comme quelque chose à l'extérieur du Simple TDD, allez-y. Par exemple, vous pourriez avoir un processus pour déclarer un test "mauvais", qui peut être appliqué dans ce cas pour supprimer le test sur les motifs qu'il teste pour quelque chose qui ne devrait pas être là (la fonction inutile b()).

Je pense que dans la pratique, la plupart des gens permettent probablement de mener une certaine quantité de refonte à effectuer avec un cycle rouge/vert/refacteur, ou envisagez de supprimer des tests d'unités redondants pour être une partie valide d'un "refacteur" même bien que strictement parlant Ce n'est pas un refactoring. Votre équipe peut décider de la quantité de drame et de papier doit être impliquée dans la justification de cette décision.

Quoi qu'il en soit, si b() était important, il y avait des tests fonctionnels pour cela, et ceux qui ne seraient pas supprimés légèrement, mais vous avez déjà dit que des tests unitaires. Si vous ne faites pas de distinction correctement entre les tests d'unités (écrites sur la conception interne actuelle du code, que vous avez modifiées) et des tests fonctionnels (écrites aux interfaces publiées, que vous ne voulez peut-être pas changer), vous devez être plus prudent à propos de l'élimination des tests d'unité.

2
Steve Jessop

Une chose à essayer de toujours vous rappeler, c'est que nous utilisons maintenant des référentiels de code avec le contrôle de version. Ce code supprimé n'est pas réellement parti ... c'est toujours là quelque part dans une itération précédente. Alors soufflez-le! Soyez libéral avec la touche DELETE, car vous pouvez toujours revenir en arrière et récupérer cette précieuse méthode élégante que vous pensiez être utile un jour ... si ce jour vient jamais. C'est là.

Bien sûr, cela va bien avec la mise en garde des maux et du danger des versions compatibles non à l'envers ... Des applications externes figurant sur votre implémentation d'interface, qui sont désormais orphelinées par votre code (soudain) obsolète.

2
dwoz