tester du legacy code, mission impossible ?
Post on 11-Apr-2017
1.078 Views
Preview:
TRANSCRIPT
Tester du Legacy Code, mission impossible ?
Bon après-midi, Ethan.
• Votre mission, si vous l’acceptez, consiste à vous occuper d’un système informatique qui n’est pas nouveau ni trop vieux, mais qui n’a aucun tests automatisé.
• Par contre, il est au cœur de plusieurs traitements d’affaires critiques. Vous allez avoir à ajouter de nouvelles fonctionnalités et a en faire l’entretien.
• Nous avons observés les choses suivantes:– Classes de la logique d’affaire qui fait 32 000 lignes.
– Une interface de 2000 lignes (incluant une ligne de commentaire par méthode) été créé récemment pour vouspermettre d’introduire des tests.
• Ce message s’auto-détruira dans 5 secondes …..
Bon après-midi, Ethan.
• Votre mission, si vous l’acceptez, consiste à vous occuper d’un système informatique qui n’est pas nouveau ni trop vieux, mais qui n’a aucun tests automatisé.
• Par contre, il est au cœur de plusieurs traitements d’affaires critiques. Vous allez avoir à ajouter de nouvelles fonctionnalités et a en faire l’entretien.
• Nous avons observés les choses suivantes:– Classes de la logique d’affaire qui fait 32 000 lignes.
– Une interface de 2000 lignes (incluant une ligne de commentaire par méthode) été créé récemment pour vouspermettre d’introduire des tests.
• Ce message s’auto-détruira dans 5 secondes …..
MISSION
Qui sommes-nous ?
• Karl Métivier
– Architecte Logiciel
– Développeur
– Coach Agile
– Formateur
• Yves St-Hilaire
– Soutien au développement
– Accompagnement, mentorat
– Développement BI
Histoire d’un système
• On change notre processus, on passe à Scrum.
• On nous demande d’ajouter des tests unitaires automatisés au code existant.– Oups, le système n’a pas été prévu
pour cela…
• Comment avoir le temps pour cela ?– Réunion pour discuter des bogues
courants.– Documentation à mettre à jour.
• Au final, problème de dette technique.
Code Legacy
C’est quoi pour vous ?
Définitions pour le Legacy Code
• Du code Legacy, c’est du code:– sans tests unitaires - Michael Feathers– plus vieux que ceux qui y travaillent– modifications en mode « patchage »– nécessitant un guide du marais pour
s’y retrouver!
• Enfreint constamment les principes SOLID
• On n’ose pas y toucher.• Et les Legacy Systems ?
Code « Legacy »
Quand vous avez à travailler avec ça, c’estquoi votre feeling ?
Pourquoi est-on pognés avec ça ?
• Délais courts de développement
• Plusieurs personnes y sont passées
• Conception Legacy?
–Architecture identique au dossier fonctionnel
• Peu d’efforts alloués pour l’amélioration
–Le système fonctionne, on n’y investit rien
Quel est le problème au juste ?
Architecture incohérente
La conception n’est pas conçue pour les tests
Dépendances cachées
Code «pas propre» et bien d’autres
Le dilemme du refactoring
• Pour faire du refactoring, on doit avoir des tests
• Pour mettre en place des tests, on doit refactoriser.
Tester le Legacy Code:1 - Comment le faire ?
Cas 1 – Classe sans injection de dépendances
• Pas de possibilité d’injecter un mock
• Plusieurs appelants l’utilisent, souvent on ne peut les modifier
Cas 1 – Classe sans injection de dépendancesComment ?
• Pour la classe qu’on veut mocker :
– Ajout d’une interface
• À la classe à tester :
– Ajout d’un constructeur permettant l’injection
– On lui injectera l’instance à utiliser, ou une Factory
• Les appelants ne sont pas impactés
• On permet l’injection, mais les appelants existants ne l’utilisent pas
Cas 1 – Ajout d’injection – Comment
public class MyClass {
private IFileReader reader;
public MyClass(string fileName) {
// original code
// ...
reader = new MyFileReader(fileName);
}
public MyClass(IFileReader injectedReader) {
// original code
// ...
reader = injectedReader;
}
}
Cas 1b – Classe sans injection de dépendancesComment ?
• Autre technique : Patron « Test Specific Subclass »
• Classe à mocker : ajout d’une interface à la classe
• Classe à tester :
– Ajout d’une méthode virtuelle « ObtenirInstanceClasseAppelee »
• Dans le projet de test :
– Hériter de la classe à tester
– Implémenter notre propre « ObtenirInstanceClasseAppelee »
Cas 1b – Test specific subclass – Comment?
public class MyClass {protected int someProtectedInfo = 42;private IFileReader reader;public MyClass(string fileName) {
reader = GetReader(fileName);}protected virtual IFileReader GetReader(string fileName) {return new MyFileReader(fileName);
}}
public class MyClassSpecificSubclass : MyClass {public IFileReader InsertMockHere;public MyClassSpecificSubclass(IFileReader aReader) {
InsertMockHere = aReader;}protected override IFileReader GetReader(string fileName) {
return InsertMockHere;}public int GetInternalInfo() {
return someProtectedInfo;}}
Cas 2 – La classe iceberg
• Reconnaissable facilement
– Grosse
• Milliers, dizaines de milliers de lignes
– Peu de méthodes publiques
• Comptées sur une seule main
• C’est quoi le problème?
– Grande complexité cyclomatique
– Interdépendances entre les méthodes
– Donc difficile à tester
Cas 2– La classe iceberg – Comment?
• Identifier les différentes responsabilités
– Noms de méthodes
– #Region ou zones de commentaires
• Refactoriser en plusieurs classes
• Si impossible :
– Identifiez les méthodes qui seraient publiques avec un refactoring
– Changer la visibilité et testez ces méthodes
Cas 2– La classe iceberg – Exemple (classe
simplifiée!)
Cas 3 – Méthode statique
• Une méthode statique, l’équivalent d’un singleton
• Une seule version possible pour tout le système
– Donc pas mockable
• Pas un problème pour la tester
• Mais un véritable problème pour tester les méthodes qui l’appellent
Cas 3 – Méthode statique – Comment?
• Ajout d’une interface à la classe
• Ajout de méthodes d’instance correspondant aux méthodes statiques
– DRY : la méthode d’instance peut appeler la méthode statique!
– Les appelants ne sont pas affectés
• Éventuellement on pourra enlevercomplètement les méthodes statiques
Cas 3 – Méthode statique – Code
public class ClassUnderTest{
public void MethodUnderTest(){
// ...var number = AnotherClass.GetNumber();if (number > 0){ }else{ }// ...
}}public class AnotherClass{
public static int GetNumber(){
return 4;}
}
Cas 3 – Méthode statique – Code
public class ClassUnderTest {private IAnotherClass _anotherClass;
public ClassUnderTest() { _anotherClass = new AnotherClass(); }public ClassUnderTest(IAnotherClass anotherClass) { _anotherClass = anotherClass; }
public void MethodUnderTest() {// ...var number = _anotherClass.GetNumber();if (number > 0){ }else{ }// ...
}}
public class AnotherClass: IAnotherClass {public static int GetNumber() { return 4; }
int IAnotherClass.GetNumber() {return AnotherClass.GetNumber();
}}
Mais en avez-vous vraiment besoin ?
• Parfois, l’ajout de tests unitaires demande beaucoup d’efforts pour peu de gains
• Évaluer d’autres opportunités
– CodedUI / Selenium
– Tests intégrés, mockeruniquement les données
– Tests comparatifs
• Pas toujours les meilleures pratiques, mais faut s’adapter au contexte!
Comment le BDD m'a aidé dans mon débogage
• Partir d’un cas d’utilisation (déjà implémenté) et le descendre en étape BDDdu début (contrôleur UI), passer par le service et aboutir dans une BD en mémoire
• C’est long
• On vérifie toutes les dépendances et on traverse toutes les couches
• C’est long, mais après coup notre compréhension du code s’est vraiment améliorée
• En plus, le test BDD reste et peut être rejoué !
État d’esprit pour déboguer de Legacy Code
• Voir le code comme une scène de crime
• Faire un profil de l’agresseur (ou de ce qui cause le problème)
• Analyser les hot-spots
• Calculer la complexité
• Faire la dissection de l’architecture
• Cartographier une carte de connaissances de votre système
• Impact sur l’existant
Tester le Legacy Code:2 - Comment avoir le droit ?
Versus la résistance
C’est impossible d’en faire chez nous car…
• Nous autres, c'est différent
• On a un système de mission critique à l'entreprise
• Nos utilisateurs ont le contrôle et le budget
• Notre système est très complexe
• On a des données nominatives
• …
C’est notre devoir d’être professionnel
Du code propre n’apparaît pas de lui-même:
• Vous avez à le produire• Vous avez à le maintenir• Vous en faites un
engagement professionnel
Combattre le résistance en discutant
• La plupart des gestionnairesdéfendent leurs échéanciers et les requis avec passion.– Cela fait partie de leurs responsabilités.
• Votre responsabilité :– Défendre également le code avec
passion.
• Ayez le courage de dire la vérité et de travailler pour arriver à un terrain d’entente.
• Discuter avec votre PO ou votre chef d’équipe. Il devrait être sensible à la qualité produite. Donc de supporter l’équipe quand il sent qu’un refactoring est nécessaire.
Il faut alors être stratégique
• Cela ne donne pas grand chose de travailler sur du code qui va bien et qui n’a pas de problème régulièrement. Don’t fix it, if it’s not broken.
• Trouver les points chauds de votre système:
– Bogues réguliers
– Difficiles à travailler
– Modification à faire prochainement
– Logique d’affaires importante
• Progresser par la technique des petits pas.
Bien évaluer
Valeur de cet investissement :
• Potentiel• Bloquant actuel• Criticité du système• Nombre de personnes impactées• Durée de vie du système• Refonte prévue?
La résistance peut donc être combattue
• C’est souvent le premier pas le plus difficile.
• Ne pas oublier :
– Être stratégique
– Évaluer le tout
• Y aller un test à la fois:
– Et son Refactoring de code qui le suit.
En fait, on en revient à la transparence
Le premier pilier de l’agilité!
3. Comment tester du Legacy Code au jour le jour
et s’en prémunir?
Comprendre le code
• Les tests nous apportent une façon de comprendre le code.
• D’une certaine manière, les tests nous font travailler dur pour arriver à cettecompréhension.
• Il est facile de faire des tests enprésence d’une bonne conception.
Ne pas hésiter à utiliser des outils modernes
• Legacy != outils désuets
• Exemples :
– Intégration continue
– Dernière version de l’IDE
– Dernières versions des librairies et frameworks
– Système Legacy dans Docker
Ne pas oublier les bases
• OOP
• Clean Code
• Maîtrisez et appliquez les principes SOLID
• Boy Scout Rule
• Agile Modeling (Scott Ambler)
Se prémunir contre le Legacy Code à la base
• Les développeurs doivent demander Quoi, Pourquoi et pour Qui avant de trouver le Comment.
• Ce n’est pas aux analystes et utilisateurs de leur dire le Comment.
• On veut savoir du client/PO qu’est-ce qu’ils veulent, pourquoi ils le veulent et pour qui cela va être utile.
Faire la conception en équipe
• Les meilleures conceptions sont effectuées en équipe, et non par une seule personne.
• La compréhension de l’architecture ainsi que son adoption est répandue dans toute l’équipe.
• Pourquoi s’en passer ?
Ou du Mob Programming !
• Approche où l’équipe complète (client, analyste et PO aussi) travaille sur la même chose :
– Au même endroit
– En même temps
– Sur le même ordinateur
• Un peu similaire au pair programming
En résumé, nous avons vu…
• C’est quoi du Legacy Code
• Comment faire pour le tester
• Comment affronter la résistance
• Comment s’en prémunir au jour le jour
Alors, tester du du Legacy Code,
• Met à l’épreuve et renforce nos
habilités en:
– Programmation Orientée-Objet
– Conception & Architecture
• Offre un challenge qui peut donc
être instructif et amusant !
• Nous rend plus professionnel en bout
de ligne.
Comment vous en sortez-vous avec le Legacy Code ?
Références 1/2
• Podcast Hanselminutes 2016-08-05 « Learning to love Legacy Code with Andrea Goulet from CorgiByte »
– http://hanselminutes.com/539/learning-to-love-legacy-code-with-andrea-goulet-from-corgibytes
• Patron « Test Specific Subclass »
– http://xunitpatterns.com/Test-Specific%20Subclass.html
• XKCD Goto
– https://xkcd.com/292/
Autres références
• Lectures suggérées
• Beaucoup de
connaissances et
d’information sur le
‘net
Pour nous rejoindre
• Karl Métivier
– Courriel: kmetivier@facilite.com
– LinkedIn: https://www.linkedin.com/in/karlmetivier/fr
– Twitter: @karlmet
– Blogue: https://excellenceagile.com/
• Yves St-Hilaire
– Courriel: yves.sthilaire@gmail.com
– LinkedIn: www.linkedin.com/in/yves-st-hilaire
– Twitter: @sthiy
top related