L'ajout de tests unitaires a-t-il un sens pour le code hérité bien connu?

21
  • Je parle de tests unitaires au sens TDD. ("Intégration" non automatisée, ou ce que vous aimez appeler des tests.)
  • Code hérité comme dans: (C ++) code sans tests. (voir: Michael Feathers travaille efficacement avec Legacy Code )
  • Mais aussi le code hérité comme dans: Code avec lequel notre équipe travaille depuis 10-5 ans, donc nous avons très souvent une assez bonne idée de l'endroit où mettre les choses pour changer quelque chose.
  • Nous avons des tests unitaires en place (via Boost.Test) pour certains modules qui sont venus plus tard ou qui ont été un ajustement "naturel" pour les tests unitaires (conteneurs spécifiques aux applications courantes, trucs de chaîne, aides réseau, etc.)
  • Nous n'avons pas encore de tests d'acceptation automatisés appropriés.

Maintenant, récemment, j'ai eu le «plaisir» de mettre en œuvre 3 nouvelles fonctionnalités destinées aux utilisateurs.

Chacun d'eux m'a pris environ 1 à 2 heures pour me familiariser avec les parties de code que je devais changer, 1 à 2 heures pour implémenter le (petit) code que je devais changer et encore 1 à 2 heures pour m'assurer que l'application a fonctionné correctement par la suite et a fait ce qu'il était censé faire.

Maintenant, j'ai vraiment ajouté peu de code. (Je pense qu'une méthode et quelques lignes d'appel pour chaque fonctionnalité.)

La factorisation de ce code (via l'une des méthodes suggérées dans WEwLC ), de sorte qu'un test unitaire aurait eu du sens (et n'aurait pas été une tautologie complète) aurait facilement pris encore 2 à 4 heures, sinon plus. Cela aurait ajouté 50% à 100% de temps à chaque fonctionnalité, sans aucun avantage immédiat, comme

  1. Je n'avais pas besoin du test unitaire pour comprendre quoi que ce soit sur le code
  2. Les tests manuels représentent la même quantité de travail, car j'ai encore besoin de tester si le code est correctement intégré dans le reste de l'application.

Certes, si , plus tard, "quelqu'un" venait et touchait ce code, il pourrait théoriquement bénéficier de ce test unitaire. (Seulement en théorie, car cette île de code testée vivrait dans un océan de code non testé.)

Donc, "cette fois-ci", j'ai choisi de ne pas faire le travail difficile de l'ajout d'un test unitaire: les modifications de code pour mettre ces choses sous test auraient été beaucoup plus complexes que les modifications de code pour obtenir la fonctionnalité correctement (et proprement) implémentée.

Est-ce quelque chose de typique pour un code hérité fortement couplé? Suis-je paresseux / fixons-nous les mauvaises priorités en équipe? Ou suis-je prudent, ne testant que des choses où les frais généraux ne sont pas trop élevés?

Martin Ba
la source
que se passe-t-il si un autre service reprend votre "code hérité bien connu"? Qu'est-ce que les gars en feront?
Alex Theodoridis

Réponses:

18

Il faut être pragmatique face à ces situations. Tout doit avoir une valeur commerciale, mais l'entreprise doit vous faire confiance pour juger de la valeur du travail technique. Oui, les tests unitaires présentent toujours un avantage, mais cet avantage est-il suffisamment important pour justifier le temps passé?

Je me disputerais toujours sur le nouveau code mais, sur le code hérité, vous devez faire un jugement.

Êtes-vous souvent dans ce domaine du code? Ensuite, il y a lieu d'améliorer continuellement. Faites-vous un changement significatif? Ensuite, il y a un cas où c'est déjà un nouveau code. Mais si vous créez un code à une ligne dans un domaine complexe qui ne sera probablement pas touché à nouveau pendant un an, bien sûr, le coût (sans parler du risque) de la réingénierie est trop élevé. Tapez simplement votre ligne de code et allez prendre une douche rapidement.

Règle générale: pensez toujours à vous-même: " Est-ce que je pense que l'entreprise profite davantage de ce travail technique que je pense que je devrais faire que du travail qu'elle a demandé et qui sera par conséquent retardé? "

pdr
la source
Qu'est-ce qu'un bon morceau de code? Celui qui est bien conçu, testé, documenté et à l'épreuve du temps ou celui pour lequel vous êtes payé? Comme toujours: vous pouvez le faire bien ; vous pouvez l'avoir bon marché ; vous pouvez l'avoir rapidement . Choisissez deux, le troisième est votre coût.
Sardathrion
2
"Je discuterais toujours du nouveau code" ... "nouveau code" dans une base de code héritée ou "nouveau nouveau code"? :-)
Martin Ba
pdr a la bonne idée. C'est une décision d'ingénierie (une avec des compromis). Généralement, si vous avez un gros morceau de code approuvé, il est préférable de le laisser seul. J'ai vu un effort TDD sur le code hérité (seulement un an, mais cela suffit) finir par coûter des mois et ne finir par dépenser de l'argent et casser une application héritée pendant des mois jusqu'à ce que les bogues introduits par le refactoriseur TDD soient corrigés. Si le code / la fonctionnalité est fait et testé, n'allez pas le casser pour ajouter de l'instrumentation (TDD) pour vous dire quelque chose que vous saviez déjà de toute façon (que cela fonctionne).
Anon
10

Le gain des tests unitaires au sens TDD est d'obtenir quelque chose qui se tient tout seul, sans avoir besoin d'une tradition orale construite au fil des ans par une équipe stable.

C'est une grande chance que la même équipe travaille sur le même projet depuis si longtemps. Mais cela finira par changer. Les gens tombent malades, s'ennuient ou sont promus. Ils bougent, prennent leur retraite ou meurent. Les nouveaux viennent avec de nouvelles idées et l'envie de refaçonner tout ce qu'ils ont appris à l'école. Les entreprises sont vendues et achetées. Les politiques de gestion changent.

Si vous voulez que votre projet survive, vous devez le préparer aux changements.

mouviciel
la source
3

L'écriture de tests unitaires permet de pérenniser votre code. Si la base de code n'a pas de tests, vous devez ajouter de nouveaux tests pour toutes les nouvelles fonctionnalités, car cela rend ce morceau de code juste un peu plus robuste.

Je trouve que l'ajout de tests, même dans du code hérité, est bénéfique à moyen et long terme. Si votre entreprise ne se soucie pas du moyen à long terme, je chercherais une autre entreprise. De plus, je ne pense pas qu'il soit plus long de tester manuellement que de rédiger un test unitaire défini. Si c'est le cas, vous devez pratiquer le kata de code axé sur l'écriture de tests.

Sardathrion - Rétablir Monica
la source
1
Mais, mais, mais! :-) Ce peu devrait se traduire par une augmentation de 100% du temps nécessaire pour implémenter une nouvelle fonctionnalité. Cela ne diminue pas le temps nécessaire pour implémenter les fonctionnalités suivantes. Cela peut réduire le temps nécessaire lors du changement de trucs.
Martin Ba
3
Vous vous assurez que les changements dans cette zone du code n'introduisent pas de nouveaux bogues, que les nouveaux (utilisateurs moins expérimentés) peuvent avoir une compréhension claire du code, et que les effets secondaires affectant cette zone sont de la toux au moment du test plutôt qu'à la sortie. temps. Les tests permettent de vérifier que le code fait ce qu'il devrait ne pas faciliter l'ajout ultérieur de fonctionnalités. Je trouve que l'ajout de tests, même dans du code hérité, est bénéfique à moyen et long terme . Si votre entreprise ne se soucie pas du moyen à long terme, je chercherais une autre entreprise.
Sardathrion
1
@Martin, car clairement ce que vous faites est de coder ce que vous pensez que la fonctionnalité devrait être, puis de l'envoyer. Aucun test n'est effectué à aucun stade ... non, attendez. En tant que développeurs, nous testons tous notre code (au moins à la main) avant de dire que c'est fait. Remplacer "à la main" par "en écrivant un test automatisé" n'est pas une augmentation de 100% du temps. Souvent, je trouve que c'est à peu près le même temps.
Kaz Dragon
1
@Kaz - "Remplacer" à la main "par" en écrivant un test automatisé "n'est pas une augmentation de 100% du temps. Souvent, je trouve que c'est à peu près le même temps." - YMMV, mais pour ma base de code, c'est clairement faux (comme indiqué dans la question).
Martin Ba
3
@Kaz: Je voulais dire :-): Un test unitaire teste mon code isolément. Si je n'écris pas de test unitaire, je ne teste pas le code isolément, mais uniquement si le code est appelé correctement et produit le résultat souhaité dans l'application. Ces deux points (appelés correctement et app-do-what-it-should) doivent quand même être testés (manuellement) et ne sont pas couverts par un test unitaire. Et le test unitaire ne couvrirait pas ces deux points mais testerait "simplement" ma fonction de manière isolée. (Et ce test unitaire serait supplémentaire et ne serait compensé par rien pour autant que je puisse voir.)
Martin Ba
2

Il est certain que les meilleures pratiques vous obligent à tester unitaire tout code que vous modifiez. Le codage dans le monde réel implique cependant beaucoup de compromis, le temps et les matériaux sont finis.

Ce que vous devez faire est d'évaluer le coût en termes réels de correction de bugs et de modifications sans tests unitaires. Vous pouvez ensuite évaluer l'investissement dans la création de ces tests. Les tests unitaires réduisent considérablement le coût des travaux futurs mais ont un prix maintenant.

En fin de compte, si votre système est rarement modifié, cela peut ne pas valoir la peine d'écrire des tests unitaires, mais s'il est soumis à des changements réguliers, cela en vaudra la peine.

Tom Squires
la source
1

Toutes ces petites décisions rationnelles de ne pas créer de tests accumulent une dette technique paralysante qui reviendra vous hanter lorsque quelqu'un partira et qu'une nouvelle embauche devra arriver et se mettre au courant.

Oui, l'écriture des tests unitaires aurait pu coûter 2-3 heures de plus, mais si le code est renvoyé des tests, vous devrez tout tester à nouveau manuellement et documenter vos tests à nouveau - vous le faites, n'est-ce pas? Sinon, comment sait-on ce que vous avez testé et quelles valeurs vous avez utilisées?

Les tests unitaires documentent le comportement attendu du code, les données de test utilisées pour vérifier la fonctionnalité et donnent aux nouveaux codeurs une assurance raisonnable qu'ils n'ont pas cassé quelque chose lors des modifications.

Aujourd'hui, cela coûte quelques heures de plus que les tests manuels, mais vous testez chaque fois que vous effectuez des modifications, ce qui vous fait gagner des heures pendant la durée de vie du code.

Cependant, si vous savez que le code sera retiré dans quelques années, tout ce que j'ai dit est que vous ne finirez pas de mettre des tests dans le code et que cela ne sera jamais remboursé.

mcottle
la source
"et documentez vos tests, encore une fois - vous le faites, n'est-ce pas?" - {mettre un chapeau cynique:} Bien sûr que non. Nous ne sommes pas dans le pays des fées des développeurs. Stuff est mis en œuvre, testé et expédié. Plus tard, il est cassé et réparé et testé et expédié.
Martin Ba
J'espère que tout sera testé manuellement quel que soit le nombre de tests unitaires. Les tests d'acceptation automatisés sont une tout autre affaire. Mais les tests d'acceptation ne sont pas compliqués par le fait que la base de code soit héritée, ils sont donc hors de portée ici.
pdr
2
mcottle - Il vous manque un point vital . Le test unitaire ne diminuera pas le temps nécessaire pour effectuer un "test de fonctionnalité manuel" de la fonctionnalité terminée. Je dois toujours cliquer sur toutes les boîtes de dialogue impliquées avec un sous-ensemble prudent d'entrées possibles pour m'assurer que l'application finie telle qu'elle est construite fait ce qu'elle est censée faire. (J'accorde que les tests unitaires théoriques peuvent aider à limiter ce cycle de test manuel à une seule boucle si les tests unitaires s'assurent que je ne trouverai aucun problème évident uniquement pendant les tests manuels.)
Martin Ba
0

La valeur des tests unitaires ne réside pas seulement dans le test d'une nouvelle fonctionnalité lorsqu'elle est développée. L'avantage des tests unitaires est principalement acquis à l'avenir.

Oui, vous devrez peut-être passer ce qui semble être un temps excessif pour rendre votre code hérité testable.

Mais si vous ne le faites pas, vous passerez, ou vous devriez certainement , passer de très nombreuses heures à tester la fonctionnalité. Pas seulement sur son développement initial, mais avec chaque nouvelle version de votre logiciel. Sans tests unitaires et autres formes de tests automatisés, vous n'avez pas d'autre option que de passer de nombreuses heures de tests manuels pour vous assurer que votre fonctionnalité n'a pas été interrompue par inadvertance, même si cette fonctionnalité elle-même n'a pas été modifiée.

Marjan Venema
la source
1
"Sans tests unitaires, vous n'avez aucun autre moyen de vous assurer que votre fonctionnalité n'a pas été interrompue par inadvertance, même si cette fonctionnalité elle-même n'a pas été modifiée." - Les tests unitaires n'aideront pas. Pour être vraiment sûr , je devrais avoir des tests d'intégration / acceptation automatisés.
Martin Ba
Oui, les tests unitaires ne sont que la première étape. Réponse modifiée pour clarifier
Marjan Venema