Tester du "Legacy Code", mission impossible ? - Karl Métivier

53
Tester du Legacy Code, mission impossible ?

Transcript of Tester du "Legacy Code", mission impossible ? - Karl Métivier

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: [email protected]

– LinkedIn: https://www.linkedin.com/in/karlmetivier/fr

– Twitter: @karlmet

– Blogue: https://excellenceagile.com/

• Yves St-Hilaire

– Courriel: [email protected]

– LinkedIn: www.linkedin.com/in/yves-st-hilaire

– Twitter: @sthiy