Je suis chargé d'obtenir une application héritée sous test unitaire. Tout d'abord quelques informations sur l'application: il s'agit d'une base de code Java RCP 600k LOC avec ces problèmes majeurs
- duplication massive de code
- pas d'encapsulation, la plupart des données privées sont accessibles de l'extérieur, certaines des données commerciales ont également fait des singletons donc elles ne sont pas seulement modifiables de l'extérieur mais aussi de partout.
- pas d'abstractions (par exemple pas de modèle commercial, les données commerciales sont stockées dans Object [] et double [] []), donc pas d'OO.
Il existe une bonne suite de tests de régression et une équipe d'AQ efficace teste et trouve des bogues. Je connais les techniques pour le tester dans des livres classiques, par exemple Michael Feathers, mais c'est trop lent. Comme il existe un système de test de régression fonctionnel, je n'ai pas peur de refactoriser agressivement le système pour permettre l'écriture de tests unitaires.
Comment dois-je commencer à attaquer le problème pour obtenir rapidement une couverture , donc je peux montrer les progrès à la direction (et en fait commencer à gagner du filet de sécurité des tests JUnit)? Je ne veux pas utiliser d'outils pour générer des suites de tests de régression, par exemple AgitarOne, car ces tests ne testent pas si quelque chose est correct.
la source
Réponses:
Je crois qu'il y a deux axes principaux le long desquels le code peut être placé lorsqu'il s'agit d'introduire des tests unitaires: A) dans quelle mesure le code est-il testable? et B) dans quelle mesure est-il stable (c.-à-d. à quel point a-t-il besoin d'urgence de tests)? En ne regardant que les extrêmes, cela donne 4 catégories:
La catégorie 1 est le point de départ évident, où vous pouvez obtenir beaucoup d'avantages avec relativement peu de travail. La catégorie 2 vous permet d'améliorer rapidement votre statistique de couverture (bonne pour le moral) et d'acquérir plus d'expérience avec la base de code, tandis que la catégorie 3 est plus (souvent frustrante), mais donne également plus d'avantages. Ce que vous devez faire en premier dépend de l'importance des statistiques de moral et de couverture pour vous. La catégorie 4 ne vaut probablement pas la peine d'être dérangée.
la source
J'ai beaucoup d'expérience en travaillant sur des systèmes hérités (pas Java cependant), beaucoup plus gros que cela. Je déteste être porteur de mauvaises nouvelles, votre problème est la taille de votre problème. Je soupçonne que vous l'avez sous-estimé.
L'ajout de tests de régression au code hérité est un processus lent et coûteux. De nombreuses exigences ne sont pas bien documentées - un correctif de bogue ici, un correctif là-bas, et avant de le savoir, le logiciel définit son propre comportement. Ne pas avoir de tests signifie que l'implémentation est tout ce qu'il y a à faire, pas de tests pour "contester" les exigences implicites implémentées dans le code.
Si vous essayez d'obtenir une couverture rapidement, il est probable que vous précipitiez le travail, le fassiez à moitié et échouiez. Les tests donneront une couverture partielle des éléments évidents et une couverture médiocre à nulle des problèmes réels. Vous convaincrez les gestionnaires que vous essayez de vendre à ce test unitaire ne vaut pas la peine, que c'est juste une autre solution miracle qui ne fonctionne pas.
À mon humble avis, la meilleure approche est de cibler vos tests. Utilisez des métriques, des intuitions et des rapports de journal des défauts pour identifier le 1% ou 10% de code qui génère le plus de problèmes. Frappez durement ces modules et ignorez le reste. N'essayez pas d'en faire trop, moins c'est plus.
Un objectif réaliste est "Depuis que nous avons implémenté l'UT, l'insertion de défauts dans les modules testés a chuté à x% de ceux qui ne sont pas sous UT" (idéalement, x est un nombre <100).
la source
Je me souviens de ce dicton de ne pas s'inquiéter de la porte de la grange lorsque le cheval a déjà boulonné.
La réalité est qu'il n'y a vraiment pas de moyen rentable d'obtenir une bonne couverture de test pour un système hérité, certainement pas dans un court laps de temps. Comme MattNz l'a mentionné, ce sera un processus très long, et finalement extrêmement coûteux. Mon instinct me dit que si vous essayez de faire quelque chose pour impressionner la direction, vous créerez probablement un nouveau cauchemar de maintenance parce que vous essayez de montrer trop rapidement, sans bien comprendre les exigences que vous essayez de tester.
De façon réaliste, une fois que vous avez déjà écrit le code, il est presque trop tard pour écrire les tests efficacement sans risquer de manquer quelque chose de vital. D'un autre côté, on pourrait dire que certains tests valent mieux qu'aucun test, mais si c'est le cas, les tests eux-mêmes doivent montrer qu'ils ajoutent de la valeur au système dans son ensemble.
Ma suggestion serait d'examiner les domaines clés où vous sentez que quelque chose est "cassé". J'entends par là qu'il pourrait s'agir d'un code terriblement inefficace, ou que vous pouvez démontrer qu'il était auparavant coûteux à entretenir. Documentez les problèmes, puis utilisez-les comme point de départ pour introduire un niveau de test qui vous aide à améliorer le système, sans vous lancer dans un effort massif de réingénierie. L'idée ici est d'éviter de rattraper les tests, et d'introduire des tests pour vous aider à résoudre des problèmes spécifiques. Après un certain temps, voyez si vous pouvez mesurer et distinguer entre le coût précédent de la maintenance de cette section de code et les efforts actuels avec les correctifs que vous avez appliqués avec leurs tests de prise en charge.
La chose à retenir est que la direction s'intéresse davantage aux coûts / avantages et à la façon dont cela affecte directement leurs clients et, finalement, leur budget. Ils ne sont jamais intéressés à faire quelque chose simplement parce que c'est la meilleure chose à faire, à moins que vous ne puissiez prouver que cela leur procurera un avantage qui les intéresse. Si vous êtes en mesure de montrer que vous améliorez le système et obtenez une bonne couverture de test pour le travail que vous effectuez actuellement, la direction est plus susceptible de voir cela comme une application efficace de vos efforts. Cela pourrait éventuellement vous permettre de plaider en faveur d'une extension de vos efforts à d'autres domaines clés, sans exiger soit un gel complet du développement du produit, ou pire encore le quasi impossible de plaider pour une réécriture!
la source
Une façon d'améliorer la couverture est d'écrire plus de tests.
Une autre façon est de réduire la redondance dans votre code, de telle sorte que les tests existants couvrent en fait le code redondant non couvert actuellement.
Imaginez que vous avez 3 blocs de code, a, b et b ', où b' est un doublon (copie exacte ou presque manquante) de B, et que vous avez une couverture sur a et b mais pas b 'avec le test T.
Si refactorisez la base de code pour éliminer b 'en extrayant les points communs de b et b' comme B, la base de code ressemble maintenant à a, b0, B, b'0, avec b0 contenant le code non partagé avec b'0 et vice- inversement, et b0 et b'0 étant beaucoup plus petits que B, et invoquant / utilisant B.
Maintenant, la fonctionnalité du programme n'a pas changé, et ni l'un ni l'autre n'a testé T, nous pouvons donc réexécuter T et nous attendre à ce qu'il passe. Le code maintenant couvert est a, b0 et B, mais pas b'0. La base de code est devenue plus petite (b'0 est plus petit que b '!) Et nous couvrons toujours ce que nous avons couvert à l'origine. Notre taux de couverture a augmenté.
Pour ce faire, vous devez trouver b, b 'et idéalement B pour permettre votre refactoring. Notre outil CloneDR peut le faire pour de nombreuses langues, notamment Java. Vous dites que votre base de code contient beaucoup de code dupliqué; cela pourrait être un bon moyen de l'aborder à votre avantage.
Curieusement, l'acte de trouver b et b 'augmente souvent votre vocabulaire sur les idées abstraites que le programme met en œuvre. Bien que l'outil n'ait aucune idée de ce que font b et b ', le fait même de les isoler du code, permettant une concentration simple sur le contenu de b et b', donne souvent aux programmeurs une très bonne idée de l'abstraction B_abstract que le code cloné implémente . Donc, cela améliore également votre compréhension du code. Assurez-vous de donner un bon nom à B lorsque vous vous en abstenez, et vous obtiendrez une meilleure couverture de test et un programme plus maintenable.
la source