Est-ce une bonne idée d'écrire tous les cas de test possibles après avoir transformé l'équipe en TDD pour obtenir une couverture complète?

17

Supposons que nous ayons une grande application de niveau entreprise sans tests unitaires / fonctionnels. Il n'y a pas eu de processus de développement piloté par les tests pendant le développement en raison de délais très serrés (je sais que nous ne devrions jamais promettre de délais serrés lorsque nous ne sommes pas sûrs, mais ce qui est fait est fait!)

Maintenant que tous les délais sont passés et que les choses sont calmes, tout le monde a accepté de nous transformer en une équipe productive basée sur TDD / BDD ... Ouais!

Maintenant, la question porte sur le code que nous avons déjà: (1) Est-ce toujours correct ou une bonne idée d'arrêter la plupart du développement et de commencer à écrire des scénarios de test possibles depuis le début, même si tout fonctionne complètement OKAY (encore!) ? Ou (2) il vaut mieux attendre que quelque chose se passe mal, puis pendant le correctif, écrivez de nouveaux tests unitaires, ou (3) oubliez même les codes précédents et écrivez simplement les tests unitaires pour les nouveaux codes uniquement et reportez tout au prochain refactor majeur.

Il y a quelques bons articles connexes comme celui-ci . Je ne sais toujours pas s'il vaut la peine d'investir dans ce domaine étant donné que nous avons un temps très limité et de nombreux autres projets / travaux nous attendent.

Remarque : Cette question explique / imagine une situation totalement délicate dans une équipe de développement. Cela ne concerne ni moi ni aucun de mes collègues; c'est juste une situation imaginaire. Vous pensez peut-être que cela ne devrait jamais arriver ou que le responsable du développement est responsable d'un tel gâchis! Mais de toute façon, ce qui est fait est fait. Si possible, ne votez pas simplement parce que vous pensez que cela ne devrait jamais se produire.

Michel Gokan
la source
6
Vous devriez probablement vous préparer à la prochaine date limite et vous n'êtes plus autorisé à faire du TDD. Peut-être en disant à celui qui a conduit le dernier cycle de développement de la dette technique pourquoi ce n'était pas une bonne idée.
jonrsharpe
1
@gnat Je pense que ce n'est pas une question en double. L'équipe mentionnée n'a aucun type de tests (pas même des tests d'intégration)
Michel Gokan
1
@gnat la question est: qu'adviendra-t-il de nos nouveaux tests unitaires? Ils peuvent sembler incomplets, voire sans valeur, sans écrire tous les tests unitaires pour les codes précédemment écrits. La question que vous mentionnez ne couvre pas cette préoccupation spécifique.
Michel Gokan
1
Il n'est pas possible d'écrire tous les cas de test possibles. Il est seulement utile d'écrire tous les cas de test qui vous intéressent. Par exemple, si vous avez besoin d'une fonction qui acceptera une intvaleur et retournera quelque chose de spécifique, il n'est pas possible d'écrire un test unitaire pour chaque intvaleur possible , mais il est probablement logique de tester une poignée de valeurs utiles qui pourraient déclencher code, tels que les nombres négatifs (y compris minint), zéro maxint, etc. pour vous assurer que certains cas de bord sont couverts.
Christopher Schultz

Réponses:

36

Il n'y a eu aucun processus de développement piloté par les tests pendant le développement en raison de délais très serrés

Cette déclaration est très préoccupante. Non pas parce que cela signifie que vous avez développé sans TDD ou parce que vous ne testez pas tout. C'est inquiétant, car cela montre que vous pensez que TDD vous ralentira et vous fera manquer un délai.

Tant que vous le voyez de cette façon, vous n'êtes pas prêt pour TDD. Le TDD n'est pas quelque chose que vous pouvez progressivement maîtriser. Soit vous savez comment le faire, soit vous ne le savez pas. Si vous essayez de le faire à mi-chemin, vous allez le faire et vous-même paraître mauvais.

TDD est quelque chose que vous devez d'abord pratiquer à la maison. Apprenez à le faire, car cela vous aide à coder maintenant . Pas parce que quelqu'un vous a dit de le faire. Pas parce que cela vous aidera lorsque vous apporterez des modifications ultérieurement. Lorsque cela devient quelque chose que vous faites parce que vous êtes pressé, vous êtes prêt à le faire de manière professionnelle.

TDD est quelque chose que vous pouvez faire dans n'importe quel magasin. Vous n'avez même pas à rendre votre code de test. Vous pouvez le garder pour vous si les autres dédaignent les tests. Lorsque vous le faites correctement, les tests accélèrent votre développement même si personne d'autre ne les exécute.

D'un autre côté, si d'autres aiment et exécutent vos tests, vous devez toujours garder à l'esprit que même dans un magasin TDD, ce n'est pas votre travail de vérifier les tests. Il s'agit de créer un code de production éprouvé. Si cela peut être testé, c'est bien.

Si vous pensez que la direction doit croire en TDD ou que vos collègues codeurs doivent prendre en charge vos tests, vous ignorez la meilleure chose que TDD fait pour vous. Il vous montre rapidement la différence entre ce que vous pensez que votre code fait et ce qu'il fait réellement.

Si vous ne voyez pas comment cela, à lui seul, peut vous aider à respecter un délai plus rapidement, alors vous n'êtes pas prêt pour TDD au travail. Vous devez vous entraîner à la maison.

Cela dit, c'est bien quand l'équipe peut utiliser vos tests pour les aider à lire votre code de production et quand la direction achètera de nouveaux outils TDD.

Est-ce une bonne idée d'écrire tous les cas de test possibles après avoir transformé l'équipe en TDD?

Peu importe ce que fait l'équipe, ce n'est pas toujours une bonne idée d'écrire tous les cas de test possibles. Écrivez les cas de test les plus utiles. La couverture à 100% du code a un coût. N'ignorez pas la loi des rendements décroissants simplement parce qu'il est difficile d'émettre un jugement.

Économisez votre énergie de test pour la logique métier intéressante. Le truc qui prend des décisions et applique la politique. Testez le diable de cela. Un code de colle structurelle évident et facile à lire qui ne fait que câbler des éléments n'a pas besoin d'être testé aussi mal.

(1) Est-ce toujours correct ou une bonne idée d'arrêter la plupart du développement et de commencer à écrire des cas de test entiers possibles depuis le début, même si tout fonctionne complètement bien (encore!)? Ou

Non, c'est la pensée "faisons une réécriture complète". Cela détruit les connaissances durement acquises. Ne demandez pas à la direction le temps d'écrire des tests. Écrivez simplement des tests. Une fois que vous savez ce que vous faites, les tests ne vous ralentiront pas.

(2) il est préférable d'attendre que quelque chose de mauvais se produise et d'écrire de nouveaux tests unitaires pendant la correction

(3) oubliez même les codes précédents et écrivez simplement des tests unitaires pour les nouveaux codes uniquement et reportez tout au prochain refactor majeur.

Je répondrai 2 et 3 de la même manière. Lorsque vous changez le code, pour une raison quelconque, c'est vraiment bien si vous pouvez glisser un test. Si le code est hérité, il n'accueille actuellement pas de test. Ce qui signifie qu'il est difficile de le tester avant de le changer. Eh bien, puisque vous le changez de toute façon, vous pouvez le changer en quelque chose de testable et le tester.

C'est l'option nucléaire. C'est risqué. Vous apportez des modifications sans tests. Il existe quelques astuces créatives pour tester le code hérité avant de le modifier. Vous recherchez ce que l'on appelle des coutures qui vous permettent de modifier le comportement de votre code sans changer le code. Vous modifiez les fichiers de configuration, créez des fichiers, tout ce qu'il faut.

Michael Feathers nous a donné un livre à ce sujet: Travailler efficacement avec le code hérité . Lisez-le et vous verrez que vous n'avez pas à brûler tout ce qui est ancien pour faire quelque chose de nouveau.

candied_orange
la source
37
"Les tests accélèrent votre développement même si personne d'autre ne les exécute." - Je trouve que c'est manifestement faux. Ce n'est vraiment pas l'endroit idéal pour entamer une discussion à ce sujet, mais les lecteurs doivent garder à l'esprit que le point de vue présenté ici n'est pas unanime.
Martin Ba
4
En fait, souvent, les tests augmentent votre développement à long terme et pour que TDD soit vraiment efficace, tout le monde doit y croire, sinon vous passeriez la moitié de votre équipe à réparer les tests cassés par les autres.
hspandher
13
"vous pensez que TDD vous ralentira et vous fera manquer un délai." Je pense que probablement est le cas. Personne n'utilise TDD car ils s'attendent à ce que leur premier délai soit respecté plus rapidement. Le véritable avantage (du moins à mon avis) est les dividendes continus qui testent le jeu à l'avenir, pour attraper les régressions et pour renforcer la confiance dans l'expérimentation en toute sécurité. Je pense que cet avantage l'emporte sur le coût initial de la rédaction des tests, la plupart seraient probablement d'accord, mais si vous devez respecter le délai serré à venir, vous n'avez pas vraiment le choix.
Alexander - Rétablir Monica
1
Je trouve cela analogue à l'achat d'une maison. Si vous aviez le montant forfaitaire pour payer une maison, vous économiseriez beaucoup sur les intérêts, et ce serait formidable à long terme. Mais si vous avez besoin d'une maison immédiatement ... alors vous êtes obligé d'adopter une approche à court terme qui n'est pas optimale à long terme
Alexander - Reinstate Monica
3
TDD = peut = augmenter les performances si les tests et le code sont développés en parallèle, tandis que la fonctionnalité est fraîche dans l'esprit du développeur. Les revues de code vous diront si un autre être humain pense que le code est correct. Les cas de test vous diront si la spécification, telle qu'incarnée dans un cas de test, est en cours d'implémentation. Sinon, oui, TDD peut être un frein, surtout s'il n'y a pas de spécifications fonctionnelles et que l'auteur du test fait également de l'ingénierie inverse.
Julie à Austin
22

Est-ce toujours correct ou une bonne idée d'arrêter la majeure partie du développement et de commencer à écrire des scénarios de test possibles dès le début [...]?

Étant donné le code hérité 1 , écrivez des tests unitaires dans ces situations:

  • lors de la correction de bugs
  • lors de la refactorisation
  • lors de l'ajout de nouvelles fonctionnalités au code existant

Aussi utile que les tests unitaires sont la création d' une complète suite de tests unitaires pour un existant 1 codeBase probablement n'est pas une idée réaliste. Les pouvoirs en place vous ont poussé à respecter un délai serré. Ils ne vous ont pas laissé le temps de créer des tests unitaires adéquats pendant votre développement. Pensez-vous qu'ils vous donneront suffisamment de temps pour créer des tests pour le "programme qui fonctionne"?

1 Le code hérité est le code sans tests unitaires. Il s'agit de la définition TDD du code hérité. Il s'applique même si le code hérité est fraîchement livré [même si l'encre n'a pas encore séché].

Nick Alexeev
la source
Mais alors, nos nouveaux tests unitaires pour de nouvelles fonctionnalités peuvent sembler incomplets, voire inutiles sans manquer de tests unitaires. N'est-ce pas?
Michel Gokan
7
(1) Sans valeur? Certainement pas. À tout le moins, ils testent la nouvelle fonctionnalité. La prochaine fois que quelqu'un voudra modifier cette fonctionnalité, il réutilisera une grande partie des tests existants. (2) Incomplet? Peut-être peut-être pas. Si vous créez également des tests unitaires qui testent la fonctionnalité héritée dont dépend la nouvelle fonctionnalité, les tests peuvent être suffisamment complets pour des raisons pratiques. En d'autres termes, créez des tests unitaires supplémentaires qui pénètrent la fonctionnalité héritée. Pénétrez à quelle profondeur? Cela dépend de l'architecture du programme, des ressources disponibles et du soutien institutionnel.
Nick Alexeev
L'inconvénient de «rédiger des tests lorsque vous en rencontrez le besoin» est qu'il y a un risque accru de se retrouver avec un patchwork de tests écrits par différents développeurs avec des idées différentes. Je ne dis pas que cette réponse est fausse, mais elle nécessite une main ferme qui maintient la qualité et le style des tests uniformes.
Flater
4
L'uniformité @Flater offre un faux confort. Je veux des tests qui rendent le code de production facile à lire. Pas des tests qui se ressemblent tous. Je pardonnerai de mélanger des cadres de test complètement différents si cela facilite la compréhension de ce que fait le code de production.
candied_orange
2
@Flater, je n'ai pas affirmé pour un code de production laid. J'affirme que le but des tests est de rendre le code de production lisible. J'accepterai volontiers une foule éclectique de tests qui facilitent la lecture du code de production. Faites attention à ne pas faire de l'uniformité un objectif en soi. La lisibilité est reine.
candied_orange
12

D'après mon expérience, les tests n'ont pas besoin d'une couverture totale pour être utiles. Au lieu de cela, vous commencez à récolter différents types d'avantages à mesure que la couverture augmente:

  • couverture de plus de 30% (alias quelques tests d'intégration): si vos tests échouent, quelque chose est extrêmement cassé (ou vos tests sont irréguliers). Heureusement, les tests vous ont alerté rapidement! Mais les versions nécessiteront toujours des tests manuels approfondis.
  • couverture à plus de 90% (c'est-à-dire que la plupart des composants ont des tests unitaires superficiels): si vos tests réussissent, le logiciel est probablement très bien. Les parties non testées sont des cas marginaux, ce qui est bien pour les logiciels non critiques. Mais les versions nécessiteront toujours des tests manuels.
  • couverture très élevée des fonctions / déclarations / branches / exigences: vous vivez le rêve TDD / BDD , et vos tests sont un reflet précis des fonctionnalités de votre logiciel. Vous pouvez refactoriser avec une grande confiance, y compris des changements architecturaux à grande échelle. Si les tests réussissent, votre logiciel est presque prêt à être publié; seuls quelques tests de fumée manuels sont nécessaires.

La vérité est que si vous ne commencez pas avec BDD, vous n'y arriverez jamais, car le travail requis pour tester après le codage est simplement excessif. Le problème n'est pas d' écrire les tests, mais plus encore d'être conscient des exigences réelles (plutôt que des détails d'implémentation fortuits) et de pouvoir concevoir le logiciel de manière à la fois fonctionnelle et facile à tester. Lorsque vous écrivez les tests en premier ou avec le code, cela est pratiquement gratuit.

Étant donné que les nouvelles fonctionnalités nécessitent des tests, mais que les tests nécessitent des modifications de conception, mais que la refactorisation nécessite également des tests, vous avez un peu un problème de poule et d'oeuf. À mesure que votre logiciel se rapproche d'une couverture décente, vous devrez effectuer une refactorisation minutieuse dans les parties du code où de nouvelles fonctionnalités se produisent, juste pour rendre les nouvelles fonctionnalités testables. Cela vous ralentira beaucoup - au début. Mais en ne refactorisant et en testant que les pièces où un nouveau développement est nécessaire, les tests se concentrent également sur le domaine où ils sont le plus nécessaires. Le code stable peut continuer sans tests: s'il était bogué, il faudrait quand même le changer.

Pendant que vous essayez de vous adapter au TDD, une meilleure mesure que la couverture totale du projet serait la couverture du test dans les parties qui sont modifiées. Cette couverture devrait être très élevée dès le départ, bien qu'il ne soit pas possible de tester toutes les parties du code impactées par une refactorisation. En outre, vous bénéficiez de la plupart des avantages d'une couverture de test élevée au sein des composants testés. Ce n'est pas parfait, mais tout de même assez bon.

Notez que bien que les tests unitaires semblent être courants, commencer par les plus petites pièces n'est pas une stratégie appropriée pour tester un logiciel hérité. Vous voudrez commencer par des tests d'intégration qui exercent une grande partie du logiciel à la fois.

Par exemple, j'ai trouvé utile d'extraire des cas de test d'intégration à partir de fichiers journaux réels. Bien sûr, l'exécution de ces tests peut prendre beaucoup de temps, c'est pourquoi vous pouvez configurer un serveur automatisé qui exécute les tests régulièrement (par exemple, un serveur Jenkins déclenché par des validations). Le coût d'installation et de maintenance d'un tel serveur est très faible par rapport à la non-exécution régulière des tests, à condition que les échecs des tests soient résolus rapidement.

amon
la source
"Plus de 90% de couverture (la plupart des composants ont des tests unitaires superficiels): si vos tests réussissent, le logiciel est probablement très bien. Les parties non testées sont des cas marginaux, ce qui est bien pour les logiciels non critiques ." Cela me semble un peu décalé, FWIW, je préférerais avoir une couverture de 30% composée principalement de cas marginaux plutôt qu'une couverture de 90% entièrement constituée du comportement attendu du chemin (ce qui est facile à faire pour les testeurs manuels); Je recommande de sortir des sentiers battus lors de l'écriture de tests et de les baser sur des cas de test (inhabituels) découverts manuellement chaque fois que possible.
jrh
5

N'écrivez pas de tests pour le code existant. Ça ne vaut pas le coup.

Ce que vous avez fait est déjà quelque peu testé de manière complètement informelle - vous l'avez constamment essayé à la main, les gens ont fait des tests non automatisés, il est utilisé maintenant. Cela signifie que vous ne trouverez pas beaucoup de bugs .

Il ne reste que les bugs auxquels vous n'avez pas pensé. Mais ce sont exactement ceux pour lesquels vous ne penserez pas écrire des tests unitaires, vous ne les trouverez donc probablement pas.

En outre, une raison pour TDD est de vous faire réfléchir sur les exigences exactes d'un peu de code avant de l'écrire. De quelque manière que ce soit, vous l'avez déjà fait.

En attendant, écrire ces tests demande toujours autant de travail que cela aurait été le cas auparavant. Cela coûtera beaucoup de temps, pour peu d'avantages.

Et c'est extrêmement ennuyeux d'écrire des tas de tests sans codage entre les deux et ne trouvant pratiquement aucun bogue. Si vous commencez à le faire, les nouveaux venus chez TDD le détesteront .

En bref, les développeurs le détesteront et les gestionnaires le verront comme coûteux, alors qu'il n'y a pas beaucoup de bogues trouvés. Vous n'obtiendrez jamais à la partie TDD réelle.

Utilisez-le sur les choses que vous souhaitez modifier, dans le cadre normal du processus.

RemcoGerlich
la source
1
Je suis fortement en désaccord avec "N'écrivez pas de tests pour le code existant. Cela n'en vaut pas la peine." Si le code fonctionne correctement, les tests peuvent être la seule spécification existante. Et si le code est en maintenance, l'ajout de tests est le seul moyen de s'assurer que les fonctions qui fonctionnent ne sont pas interrompues par des modifications apparemment sans rapport.
Julie à Austin
3
@JulieinAustin: D'un autre côté, sans spécification, vous ne savez pas exactement ce que le code est censé faire. Et si vous ne savez pas déjà ce que le code est censé faire, vous pourriez bien écrire des tests inutiles - ou pire, des tests trompeurs qui modifient subtilement la spécification - et maintenant un comportement accidentel et / ou incorrect devient nécessaire.
cHao
2

Un test est un moyen de communiquer la compréhension.

Par conséquent, seuls les tests pour ce que vous comprenez doivent être vrais.

Vous ne pouvez comprendre ce qui devrait être vrai que lorsque vous travaillez avec.

N'écrivez donc que des tests pour le code avec lequel vous travaillez.

Lorsque vous travaillez avec le code, vous apprendrez.

Par conséquent, écrivez et réécrivez des tests pour capturer ce que vous avez appris.

Rincez et répétez.

Exécutez un outil de couverture de code avec vos tests et acceptez uniquement les validations sur la ligne principale qui ne réduisent pas la couverture. Finalement, vous atteindrez un niveau de couverture élevé.

Si vous n'avez pas travaillé avec le code depuis un certain temps, une décision commerciale doit être prise. Il est peut-être maintenant si hérité que personne dans votre équipe ne sait comment travailler avec. Il a probablement des bibliothèques / compilateurs / documentation obsolètes, ce qui représente une responsabilité énorme à presque tous les égards.

Deux options:

  1. Prenez le temps de le lire, d'en tirer des leçons, d'écrire des tests et de le remanier. Petits ensembles de changements avec des versions fréquentes.
  2. Trouvez un moyen d'abandonner ce logiciel. De toute façon, vous ne pouviez pas y apporter de modification.
Kain0_0
la source
0

(1) Est-ce toujours correct ou une bonne idée d'arrêter la plupart du développement et de commencer à écrire des cas de test entiers possibles depuis le début, même si tout fonctionne complètement bien (encore!)?
(2) il vaut mieux attendre que quelque chose de mauvais se produise, puis pendant le correctif, écrire de nouveaux tests unitaires

L'un des principaux objectifs des tests est de s'assurer qu'un changement n'a rien cassé. Il s'agit d'un processus en trois étapes:

  1. Confirmer que les tests réussissent
  2. Vous apporter des changements
  3. Confirmer que les tests réussissent toujours

Cela signifie que vous devez avoir des tests de travail avant de réellement changer quelque chose. Si vous choisissez le deuxième chemin, cela signifie que vous devrez forcer vos développeurs à écrire des tests avant même qu'ils ne touchent le code. Et je soupçonne fortement que lorsqu'ils sont déjà confrontés à un changement dans le monde réel, les développeurs ne vont pas accorder aux tests unitaires l'attention qu'ils méritent.

Je suggère donc de diviser les tâches d'écriture de test et de modification pour éviter que les développeurs ne sacrifient la qualité de l'un pour l'autre.

même si tout fonctionne parfaitement OKAY (encore!)?

Juste pour le signaler spécifiquement, c'est une idée fausse courante que vous n'avez besoin de tests que lorsque le code ne fonctionne pas. Vous avez également besoin de tests lorsque le code fonctionne, par exemple pour prouver à quelqu'un que [le nouveau bug qui se produit] n'est pas dû à votre partie car les tests sont toujours réussis.
Confirmer que tout fonctionne toujours comme avant était un avantage important des tests que vous omettez lorsque vous indiquez que vous n'avez pas besoin de tests lorsque le code fonctionne.

(3) oubliez même les codes précédents et écrivez simplement des tests unitaires pour les nouveaux codes uniquement et reportez tout au prochain refactor majeur

Idéalement, tout le code source existant devrait maintenant recevoir des tests unitaires. Cependant, il existe un argument raisonnable selon lequel le temps et les efforts (et les coûts) nécessaires pour le faire ne sont tout simplement pas pertinents pour certains projets.
Par exemple, une application qui n'est plus en cours de développement et qui ne devrait plus être modifiée (par exemple, le client ne l'utilise plus ou le client n'est plus un client), vous pouvez faire valoir qu'il n'est plus pertinent de tester ce code plus .

Cependant, ce n'est pas aussi clair où vous tracez la ligne. C'est quelque chose qu'une entreprise doit examiner dans une analyse coûts-avantages. L'écriture de tests coûte du temps et des efforts, mais s'attendent-ils à un développement futur de cette application? Les gains des tests unitaires l'emportent-ils sur le coût de leur rédaction?

Ce n'est pas une décision que vous (en tant que développeur) pouvez prendre. Au mieux, vous pouvez offrir une estimation du temps nécessaire pour mettre en œuvre des tests sur un projet donné, et c'est à la direction de décider s'il y a une attente suffisante d'avoir réellement besoin de maintenir / développer le projet.

et reporter tout au prochain refactor majeur

Si le prochain refactor majeur est une donnée, vous devez en effet écrire les tests.

Mais ne le remettez pas avant que vous ne soyez confronté à des changements majeurs. Mon point de départ (ne pas combiner l'écriture de tests et la mise à jour du code) est toujours valable, mais je veux ajouter un deuxième point ici: vos développeurs connaissent actuellement mieux leur chemin dans le projet qu'ils ne le seront en six mois s'ils passent ce temps à travailler sur d'autres projets. Tirez parti des périodes où les développeurs sont déjà au chaud et n'ont pas besoin de comprendre comment les choses fonctionneront à nouveau à l'avenir.

Flater
la source
0

Mes deux centimes:

Attendez une mise à niveau technique majeure du système et écrivez ensuite les tests ... officiellement avec le soutien de l'entreprise.

Alternativement, disons que vous êtes un magasin SCRUM, votre charge de travail est représentée par la capacité et vous pouvez allouer un% de cela aux tests unitaires, mais ...

Dire que vous allez revenir en arrière et écrire les tests est naïf, ce que vous allez vraiment faire est d'écrire des tests, de refactoriser et d'écrire plus de tests après que le refactor a rendu le code plus testable, c'est pourquoi il est préférable de commencer avec des tests comme vous le savez déjà, et ...

Il est préférable que l'auteur d'origine écrive des tests et remanie le code qu'ils ont écrit précédemment, ce n'est pas l'idéal, mais d'après l'expérience, vous voulez que le refactoriste améliore le code et n'empire pas.

RandomUs1r
la source