Affichage des articles dont le libellé est XP. Afficher tous les articles
Affichage des articles dont le libellé est XP. Afficher tous les articles

jeudi 30 septembre 2010

Tester unitairement du “Legacy Code” : un exemple

(Note au utilisateur de GoogleReader : Pour une raison inconnue, les couleurs ne sortent pas … lisez plutôt ici)

 

Je souhaite vous montrer dans ce billet un exemple d’ajout de test unitaire dans du legacy code. Du legacy code, c’est du code non testé automatiquement. Je travaille tous les jours sur du code non couvert par des tests automatiques alors capitaliser en ajoutant petit à petit des tests est un réel enjeu pour éviter les régressions.

 

Le Problème

 

Voici une classe développée il y a presque 10 ans pour dessiner des annotations sur un graphique. Il se trouve que dans certaines  rares conditions, l’annotation sort du graphique et ceci ne fait pas propre lors de son impression. Nous avons donc souhaité corriger ce défaut.

__________________________________________________________________

TSKRAnnotDrawer = class

 private

   FAnnotList: TList;

   FSquaring: TSquaringType;

   FHorizontalMaille: Real;

   FVerticalMaille: Real;

 

   procedure Sort;

   function GetAnnot(Index: Integer): PAnnotRec;

   procedure FindCorrespondingSquaringRectangle(aRect: TRect;

X, Y: Integer;

var SquaringRow, SquaringCol: Integer);

   procedure CalculateSquaringRectanglesNumber(aRect: TRect;

W, H: Integer; var SquaringWidth, SquaringHeight: Integer);

   function FindFreeSpace(SquaringRow, SquaringCol,

SquaringWidth, SquaringHeight: Integer;

var SquaringFreeRow, SquaringFreeCol: Integer;

var UpOrDown:         Boolean): Boolean;

   function TestIfItIsInTheGraphic(row, col,

SquaringWidth, SquaringHeight: Integer): Boolean;

   function TestIfTheSpaceIsFree(row, col,

SquaringWidth, SquaringHeight: Integer): Boolean;

   procedure UpdateFSquaring(row, col, SquaringWidth, SquaringHeight: Integer);

 public

   PeakAnnotation: TSKRPeakAnnotationPrefs;

   EventAnnotation: TSKREventAnnotationPrefs;

   procedure Create;

   procedure Destroy; override;

   procedure ClearList;

   procedure AddToList(aAnnot: TAnnotRec);

   procedure Draw(Canvas: TCanvas3D; arect: TRect);

 end;

__________________________________________________________________

 

Nous allons nous intéresser à la méthode “Draw”. Après investigation, l’erreur provient du calcul de la hauteur de la première annotation. En effet, la ‘font size’ est assignée après le calcul de la hauteur du texte : voir ci-dessous le texte en rouge.

__________________________________________________________________

procedure TSKRAnnotDrawer.Draw(Canvas: TCanvas3D; aRect: TRect);

var

i, Width, Height: Integer;

UpOrDown: Boolean;

w, h: Integer;

AnnotationPrefs: TSKRAnnotation;

row, col: Integer;

SquaringRow, SquaringCol: Integer;

SquaringWidth, SquaringHeight: Integer;

SquaringFreeRow, SquaringFreeCol, XFree, YFree, YFreeTemp: Integer;

begin

assert(EventAnnotation <> nil);

assert(PeakAnnotation <> nil);

 

// sort annotation according time

Sort;

Width := 0;

Height := -1;

 

 //initializing the squaring array.

 for row := 0 to Pred(SquaringSize) do // Iterate

 begin

   for col := 0 to Pred(SquaringSize) do // Iterate

   begin

     FSquaring[row, col] := True;

   end; // for

 end; // for

 

 for i := 0 to Pred(FAnnotList.Count) do

   with GetAnnot(i)^ do

   begin

  

     if Typ = 0 then

       AnnotationPrefs := EventAnnotation

     else

       AnnotationPrefs := PeakAnnotation;

 

     UpOrDown := AnnotationPrefs.AutoUpDown = apUp;

 

     if AnnotationPrefs.Orientation = aoOblique then

     begin

      //We take the size of the rectangle projection on a vertical axis

       H := round(Sin(Pi / 4) * (Canvas.TextWidth(Text) + Canvas.TextHeight(Text)));

      //and for W the size of the rectangle projection on the horizontal axis

       W := round(Cos(Pi / 4) * (Canvas.TextWidth(Text) + Canvas.TextHeight(Text)));

     end

     else if AnnotationPrefs.Orientation = aoVertical then

     begin

       H := Canvas.TextWidth(Text);

       W := Canvas.TextHeight(Text);

     end

     else //Horizontal

     begin

       H := Canvas.TextHeight(Text);

       W := Canvas.TextWidth(Text);

     end;

 

     H := H + 4;

 

    if AnnotationPrefs.AutoUpDown = apAuto then

     begin

       Canvas.Font.Name := AnnotationPrefs.Font.name;

       Canvas.Font.Size := AnnotationPrefs.Font.Size; //INTERVIENT TROP TARD

     end;

 

     if AnnotationPrefs.LinkType = ltNone then

     begin

                ... 6 lines

     end

     else

     begin

                ... 30 lines

     end;

 

     DrawAnnotation(Canvas, Text, Point(XFree, YFree),

       XFree, AnnotationPrefs, UpOrDown, Width, Height, X, Y, aRect);

   end;

end;

__________________________________________________________________

 

La correction est triviale, il suffit de remonter l’affectation de la font size avant la première utilisation du Canvas.

 

Mais comment tester ce que l’on souhaite modifier?

 

Le cheminement vers le test unitaire

 

Ma première idée fut d’instancier la classe et appeler la méthode Draw dans un test unitaire. En effet, le constructeur ne prend pas de paramètre alors ça paraissait facile. Ensuite, il a fallu ajouter des assertions à mon test et là les ennuis commençaient. En effet, je n’avais aucune envie de vérifier le rendu sur le graphique (via le Canvas) car je ne savais pas comment m’y prendre.

 

Donc je suis reparti de zéro afin d’isoler mon problème. J’ai suivi les étapes suivantes :

 

1- Extraction de la partie de code problèmatique

 

Je ne prend pas de risque à ce niveau là et je fais confiance au compilateur pour m’indiquer les éventuels problèmes.

 

__________________________________________________________________

 

procedure TSKRAnnotDrawer.getTextHeightAndWidth(

     const aCanvas: TCanvas3D;

     const aAnnotationPrefs: TSKRAnnotation;

     const aText: string;

     var oTextHeight, oTextWidth: integer);

begin

   if AnnotationPrefs.Orientation = aoOblique then

   begin

    //We take the size of the rectangle projection on a vertical axis

     H := round(Sin(Pi / 4) * (Canvas.TextWidth(Text) + Canvas.TextHeight(Text)));

    //and for W the size of the rectangle projection on the horizontal axis

     W := round(Cos(Pi / 4) * (Canvas.TextWidth(Text) + Canvas.TextHeight(Text)));

   end

   else if AnnotationPrefs.Orientation = aoVertical then

   begin

     H := Canvas.TextWidth(Text);

     W := Canvas.TextHeight(Text);

   end

   else //Horizontal

   begin

     H := Canvas.TextHeight(Text);

     W := Canvas.TextWidth(Text);

   end;

    H := H + 4;

   if AnnotationPrefs.AutoUpDown = apAuto then

   begin

     Canvas.Font.Name := AnnotationPrefs.Font.name;

     Canvas.Font.Size := AnnotationPrefs.Font.Size; //INTERVIENT TROP TARD

   end;

end;

 

__________________________________________________________________

 

Là, ça devient plus simple de tester cette fonction mais celle-ci n’est pas publique et changer l’API, c’est à dire le contrat de la classe, à des fins de tests n’est pas élegant. En effet, cette méthode n’a pas d’intéret pour l’utilisateur de la classe.

 

 

2- Extraction de la méthode vers une classe spécifique

 

En ayant en tête les principes SOLID et plus particulièrement celui qui dit qu’une classe ne doit avoir qu’une responsabilité, je n’ai pas d’état d’âme à créer une classe pour isoler ma méthode:

 

__________________________________________________________________

 

TTextPixelSizeHelper = class

public

 class procedure GetTextHeightAndWidth(

   const aCanvas: TCanvas3D;

   const aAnnotationPrefs: TSKRAnnotation;

   const aText: string;

   var oTextHeight, oTextWidth: integer);

end;

 

class procedure TTextPixelSizeHelper .getTextHeightAndWidth(

     const aCanvas: TCanvas3D;

     const aAnnotationPrefs: TSKRAnnotation;

     const aText: string;

     var oTextHeight, oTextWidth: integer);

begin

  ... idem ...

end;

__________________________________________________________________

 

 

Je ne prend toujours pas de risque et je fais confiance au compilateur pour les éventuelles erreurs de refactoring. Je n’ai changé aucun comportement.

 

La méthode Draw peu maintenant utiliser cette nouvelle classe :

 

__________________________________________________________________

procedure TSKRAnnotDrawer.Draw(Canvas: TCanvas3D; aRect: TRect);

var

...

begin

...

 for i := 0 to Pred(FAnnotList.Count) do

   with GetAnnot(i)^ do

   begin

     if Typ = 0 then

       AnnotationPrefs := EventAnnotation

     else

       AnnotationPrefs := PeakAnnotation;

 

     UpOrDown := AnnotationPrefs.AutoUpDown = apUp;

 

     TTextPixelSizeHelper.GetTextHeightAndWidth(Canvas, AnnotationPrefs, Text, H, W);

        ...

 

     DrawAnnotation(Canvas, Text, Point(XFree, YFree),

       XFree, AnnotationPrefs, UpOrDown, Width, Height, X, Y, aRect);

   end;

end;

__________________________________________________________________

 

Au passage, nous avons diminué la complexité cyclomatique de la fonction et facilité sa compréhension.

 

 

3- Ecriture d’un test qui échoue

 

Le principe du test est d’appeler deux fois de suite la fonction ‘GetTextHeightAndWidth’  avec le même texte et de vérifier qu’elle donne deux fois le même résultat.

__________________________________________________________________

  

procedure TSKRAnnotDrawerTest.testPeakNamesMightBePrintedBeOutisdeOfTheChartArea;

const

  TEXT1_TO_DRAW = 'TestPeakAnnot';

  TEXT2_TO_DRAW = TEXT1_TO_DRAW;

  PEAK_ANNOTATION_TYPE = 1;

var

  chart: TOrlandoChart;

  form: TForm;

  canvas: TCanvas3D;

  peak_annotation_prefs: TSKRPeakAnnotationPrefs;

  text1_height_result, text2_height_result: integer;

  text1_width_result, text2_width_result: integer;

begin

  peak_annotation_prefs := TSKRPeakAnnotationPrefs.Create;

 

form := TForm.Create(nil);

chart := TOrlandoChart.Create(form);

chart.ParentWindow := form.Handle;

canvas := chart.Canvas;

 

 try

   canvas.Font.Size := 4;

   assert(canvas.Font.Size <> peak_annotation_prefs.Font.size, 'Font size must be different to show the defect problem');

 

   TTextPixelSizeHelper.GetTextHeightAndWidth(

canvas, peak_annotation_prefs, TEXT1_TO_DRAW,

text1_height_result, text1_width_result);

   TTextPixelSizeHelper.GetTextHeightAndWidth(

canvas, peak_annotation_prefs, TEXT2_TO_DRAW,

text2_height_result, text2_width_result);

 

   CheckEquals(text1_height_result, text2_height_result,

'calling twice the function with same text should provide the same Height  result!');

   CheckEquals(text1_width_result, text2_width_result,

'calling twice the function with same text should provide the same Width result!');

 finally

   peak_annotation_prefs.Free;

   chart.Free;

   form.free;

 end;

end;

_________________________________________________________________

  

red

 

 

 

4- Correction du bug

 

La correction conciste à déplacer quelques lignes de code afin d’assigner la “font size” du canvas avant de s’en servir:

 

_________________________________________________________________

  

procedure TTextPixelSizeHelper.getTextHeightAndWidth(

     const aCanvas: TCanvas3D;

     const aAnnotationPrefs: TSKRAnnotation;

     const aText: string;

     var oTextHeight, oTextWidth: integer);

begin

   if AnnotationPrefs.AutoUpDown = apAuto then

   begin

     Canvas.Font.Name := AnnotationPrefs.Font.name;

     Canvas.Font.Size := AnnotationPrefs.Font.Size;

   end;

   if AnnotationPrefs.Orientation = aoOblique then

   begin

    //We take the size of the rectangle projection on a vertical axis

     H := round(Sin(Pi / 4) * (Canvas.TextWidth(Text) + Canvas.TextHeight(Text)));

    //and for W the size of the rectangle projection on the horizontal axis

     W := round(Cos(Pi / 4) * (Canvas.TextWidth(Text) + Canvas.TextHeight(Text)));

   end

   else if AnnotationPrefs.Orientation = aoVertical then

   begin

     H := Canvas.TextWidth(Text);

     W := Canvas.TextHeight(Text);

   end

   else //Horizontal

   begin

     H := Canvas.TextHeight(Text);

     W := Canvas.TextWidth(Text);

   end;

    H := H + 4;

end;

_________________________________________________________________

  

green 

 

 

La barre est verte! Donc place au refactoring si nécéssaire.

 

 

Conclusion

 

Tester du legacy code peut décourager bon nombre de développeurs. Ce n’ai pas très compliqué mais ça demande une petite gymnastique intellectuelle. Tout m’a semblé plus facile après quelques essais et la lecture du bien connu livre de Michael C. Feathers “Working Effectively with Legacy Code” . Ce n’ai pas toujours possible à un coût raisonnable alors dans notre équipe, nous ne nous demandons pas une obligation de résultat.

 

Avec quelques amis développeurs Grenoblois, nous allons présenter une session dédiée à ce sujet sur du vrai code lors de la conférence Agile Grenoble 2010; venez nous voir ;o) !

 

 

 

 

Share/Bookmark

mercredi 9 décembre 2009

3 questions de Dragos Dreptate, Responsable R&D Logiciel

Dragos, Responsable R&D Logiciel, envisage le passage aux Méthodes Agiles et à se titre se renseigne et se documente sur la question. Je lui ai proposé de partager mon expérience en répondant à quelques-unes de ses interrogations car être Agile, c'est aussi savoir partager sa connaissance. Dragos a accepté la démarche alors que nous ne nous connaissions pas! C'est un signe de confiance "à priori" que je salut; c'est une valeur très importante.

Voici le contenu de nos premiers échanges:

[Dragos]

Ma première question va dans la direction du recrutement. Il est clair, la qualité des membres de l’équipe est primordiale, bonne communication, team player et excellence technique s’imposent. La question : Comment faire pour être sur que la personne qu’on recrute est « agile » ?

[Luc]

Chez nous, voici les étapes que nous mettons en oeuvre pour recruter:
- Rédaction d'une annonce qui souligne les points essentiels sur le contexte de notre entreprise et les valeurs que nous recherchons chez notre futur collaborateur.
- Nous organisons une session de tests avec une douzaine de candidats où nous décrivons plus précisément notre entreprise. Puis il y a deux heures de tests écris sur des points techniques, logiques, d'Anglais. Ces tests ne sont pas très difficiles et nous permettent de faire un premier filtre.
- Les 3-4 candidats que nous estimons les meilleurs sont reçus en entretien individuel pour apprendre à mieux les connaître et découvrir leurs motivations et aspirations.
- Puis nous avons expérimenté une mise en situation dans une équipe sur une "journée accélérée" afin de mieux évaluer les aptitudes des candidats à travailler en équipe. Ce fut assez intéressant pour tester la capacité d'adaptation des candidats!

Quelques pistes:
http://www.cio.com/article/print/478106
http://www.agilex.fr/2009/01/recrutement-agile/


[Dragos]

La deuxième question s’attaque aux problèmes de test. L’activité de test est intégrée au sprint. Les méthodes agiles essaient de diminuer le temps alloué a cette activité en accordant plus d’attention a la qualité du design et du codage, en gros a tout ce que tienne de l’ingénierie logiciel, pour produire un meilleur code avec moins défauts. C’est bien, mais parfois pas suffisant. Nos clients, des grands laboratoires pharmaceutiques, utilisent notre application scientifique en phase de recherche de nouveaux médicaments. Une étape très sensible et coûteuse. Nous sommes soumis à des réglementations strictes concernant la qualité logicielle et sommes certifiés ISO9001. Mais au delà des certifications, la qualité du livrable est un engagement très fort que nous avons envers nos clients. Actuellement la phase de test a lieu après la fin du développement et consiste dans de plans de test « statiques », quasiment manuels, de tests qui s’appuient sur de spécifications détaillés écrites après la fin du codage. Très peu de tests unitaires. Et enfin la question : Connaisez-vous des outils de test "agiles", permettant de créer des tests automatisés adaptés aux logiciels avec beaucoup d’IHM et beaucoup d’intervention de l’utilisateur dans les scenarios d’usage ?

[Luc]

Avant de répondre à la question j'aimerais revenir sur la notion de test dans un contexte Agile. Tout d'abord, pour fixer le périmètre de mes propos, je vais m'appuyer sur deux méthodes Agiles complémentaires : SCRUM pour la gestion du projet et XP (ExtrêmeProgramming) pour les pratiques de développement. Dans ces méthodes, tout est itératif et incrémental car on recherche le feedback en permanence. Plus tôt est le retour sur les problèmes tels que les défauts où incompréhensions des besoins utilisateurs, moins cher coûtera la correction ou l'adaptation. De ce fait, les tests sont quotidiennement nécessaires à mesure que le développement évolue. Ces méthodes ne diminuent pas le temps alloué aux tests mais au contraire l'augmente. Le principe, c'est de ne pas dire qu'une fonctionnalité est terminée tant qu'elle n'est pas testée et le but recherché est de terminer des fonctionnalités dans les sprints (2-3 semaines). En d'autres termes, on ne repousse pas la phase de tests à la traditionnel Release Candidate pour y trouver des défauts qui prendront plusieurs semaines à corriger. A chaque fin d'itération, on doit avoir un incrément de produit potentiellement livrable au client donc entièrement validé et de qualité irréprochable. Alors évidement, on ne plus se permettre de dérouler des plans de tests pendant des jours entiers alors il faut changer les pratiques de test.

Il y a plusieurs types de tests à considérer:


- Les tests unitaires sont écris pendant le développement et se situes au niveau des classes. Idéalement il faut pratiquer le TDD (Test Driven Development) où on écrit toujours un test unitaire avant d'ajouter du code pour une fonctionnalité. De cette façon, on ne développe que ce qui est nécessaire (pas de provision ni de code mort) et ceci permet de faire émerger un design qui soit testable avec peu de couplage, etc. Je vous propose de lire le Tutoriel de Bruno.

- Les tests d'intégration sont également écris par les développeurs pour s'assurer que les classes fonctionnent bien ensemble.

- Les tests fonctionnels testent les critères d'acceptation de la fonctionnalité. Il est largement préférable de les automatiser car plus les projets grossissent, plus dérouler l'ensemble des tests fonctionnels en fin d'itération devient long et pénible. Sur des petits projets, il peut être acceptable de garder ces tests manuels.

Au final, il n'est pas anormal de passer les 2/3 du temps de développement à écrire des tests. Pour que le feedback soit rapide, il faut respecter la pyramide pour la répartition des tests. Les tests unitaires doivent être nombreux et couvrir un maximum de cas possibles. Ils doivent également être très rapides pour pouvoir être exécutés par tous les développeurs, tout le temps, pour vérifier que rien n'a été cassé. Il est très important de comprendre qu'automatiser ces tests permettra d'avoir des tests joués tous les jours. En effet, toutes les nuits, un système d'intégration continue doit construire l'application de A à Z (compilation, setup, etc) et exécuter tous les tests (unitaires, intégrations, fonctionnels). Ainsi, chaque matin, les équipes ont une vue claire sur l'état du projet. Si un test n'est pas passé, la priorité sera de le réparer.

Quand on travail sur une base de code non couverte par des tests, il faut apprendre à ajouter de la fonctionnalité en ajoutant des tests. Cela peut-être difficile mais plein de techniques existes pour isoler et modulariser la base de code. A ce sujet, je vous recommande la bible en la matière : Working Effectively With Legacy Code

Concernant les tests d'IHM, la pratique nous a montré que les tests manipulant les IHM sont souvent fragiles. En découplant bien la présentation des données, il est plus efficace de tester la couche juste en dessous. Ceci dit, nous utilisons pour nos projets en C# le framework de tests NUnit avec NUnitForms. Pour les applications Web, il y a Selenium (que je ne connais pas). Je vous propose de lire le billet suivant : http://blog.octo.com/demarches-de-tests-fonctionnels/

Les tests d'IHM coûtent cher par rapport à des tests plus bas niveau donc nous nous en servons pour vérifier qu'il n'y a pas de bug mais nous comptons principalement sur des tests plus bas niveau pour couvrir l'application et traiter le maximum de cas.

Il faut aussi donner de l'importance aux tests exploratoires. Ils permettent de challenger le logiciel sur toutes les couches et ne sont pas automatisables. Ce sont des tests structurés mais pas prédéfinis.


[Dragos]


J’ai pu trouver pas mal d’informations sur les points forts et les bénéfices que les méthodes agiles apportent mais il me manque une meilleure vue sur les risques engendrés par l’adoption de ces méthodes et sur leurs points faibles en général. Pouvez-vous m’en dire plus à ce sujet ?

[Luc]

Voici quelques points à considérer lors d'un passage à Scrum/XP:

- Scrum, de part sa nature itérative et empirique, ne permet pas de s'engager sur les trois points suivant en même temps

-> Contenu du produit
-> Qualité du produit.
-> Délais

Le plus souvent, c'est le contenu qui est adaptable car chacun sait qu'une grosse partie des fonctionnalités d'un logiciel ne sont jamais ou très peu utilisé. Donc c'est le jeu, le Product Owner doit faire des choix de priorité en permanence.

- Les chefs de projets et les représentants de la QA doivent changer de rôle pour laisser les équipes s'auto-organiser. Ils peuvent devenir ScumMaster (facilitateur), Product Owner si ils connaissent bien le métier, expert-technique ou ne pas trouver leur nouvelle place.

- Les Testeurs ne peuvent plus travailler comme avant. Je vous recommande le livre "Agile Testing: A Practical Guide for Testers and Agile Teams". Ceci dit, personne en ma connaissance ne regrette l'époque cycle en V avec des campagnes de tests de plusieurs semaines.

- Les équipes, avant de devenir productives, vont traverser plusieurs étapes donc il ne faut pas changer ces équipes trop souvent car le processus peut prendre plusieurs années.
-> Forming (création de l'équipe)
-> Storming (turbulence)
-> Norming (harmonisation)
-> Performing (performance)

- Il n'est pas toujours évident d'insérer les contraintes non fonctionnelles telles que l'Architecture et les Performances aux Product BackLog. Les ProductOwners ne sont pas toujours les personnes adaptées pour fournir des critères sur ces points. Le danger, c'est de considérer certains points trop tardivement.



Chers lecteurs, si vous avez des compléments d'information à partager, n'hésitez pas! Si un échange sur un point particulier vous intéresse, contactez-moi!





Share/Bookmark

vendredi 4 septembre 2009

Un test vert sans avoir été rouge sent mauvais (V2)

Contexte

Dans un contexte Agile, le développement dirigé par les tests (TDD) est incontournable. Ces tests sont le plus souvent unitaires ou d'intégrations car ils aident à construire l'application et couvrent les fonctionnalités existantes en apportant un feedback très rapide. La règle de base du TDD est l'application du cycle RED / GREEN / REFACTOR. Donc on code d'abord le test puis on le fait passer rapidement et enfin on remanie le code si besoin pour éliminer les duplications et améliorer le design.

Un test vert sans avoir été rouge ne sent pas très bon

Un test qui est vert sans passer par l'étape rouge doit interpeller le développeur. En effet, rien ne prouve alors qu'il teste ce qu'il doit tester. Plusieurs scénarios peuvent amener à ce cas :
  • Le cas que je m'apprête à coder est déjà couvert donc mon test passe immédiatement
  • Je code le test après avoir développé la fonctionnalité; ce n'est plus du TDD et on passe son temps à douter de la pertinence de ses tests.
  • Mon test ne teste pas ce qu'il devrait tester

Dans tous les cas, si le test est vert immédiatement, on doit se poser des questions. Au minimum, Il faut faire une contre vérification pour s'assurer de sa pertinence. C'est très simple, il suffit de modifier volontairement le code "under test".

Donc pour qu'un test ait de la valeur il doit nécessairement passer par l'étape rouge.

Et les tests fonctionnels alors?

Les tests fonctionnels servent à tester les "user stories" dans leur globalité en vérifiant que les critères d'acceptance sont bien remplis. Ces tests ou recettes peuvent être exécutés manuellement et l'oeil averti du testeur permet de vérifier que la fonctionnalité est conforme aux attentes du client. Le problème c'est que sur des projets de taille conséquente, cette approche n'est pas viable car le feedback à beaucoup trop long. A mes débuts de développeur, un des projets sur lesquels j'ai travaillé nécessitait 2 jours de tests à plusieurs personnes. Autant dire que lorsque qu'une régression était découverte, le sprint était compromis.

La solution est donc d'automatiser les tests fonctionnels. Oui MAIS! Mais alors attention aux tests qui seront verts sans passer par l'étape rouge! Pour factoriser la vérification de critères, mon équipe avait développé un module spécialisé. Ce module a parfaitement bien fonctionné pendant plusieurs mois. Un jour, ce module a du être modifié pour prendre en compte un changement d'architecture. Puis de nouveaux tests fonctionnels ont été ajoutés à la liste. Ces nouveaux tests ainsi que tous les autres étaient verts pendant plusieurs mois ... mais notre module ne testait plus une bonne partie des critères...

Conclusion

Un test vert sans avoir été rouge ne peut pas apporter la confiance recherchée et doit interpeller le développeur.
Share/Bookmark

mercredi 4 mars 2009

Pair-programming = efficacité + plaisir

Aujourd'hui, je n'ai pas passé une minute seul devant mon ordinateur; nous sommes actuellement trois développeurs dans l'équipe et j'ai travaillé avec tout le monde, sur chaque ordinateur.

La matinée a commencé avec un de mes collègues avec l'étude de Simian un outil qui permet de détecter les duplications de code. Nous avons travaillé une bonne heure ensemble avec pour conclusion que l'outil n'était pas forcement le plus approprié à notre langage de programmation Delphi mais que sur le principe, ça serait très intéressant d'intégrer cette métrique à Hutson, notre moteur d'intégration continue.

Puis, un collègue de l'équipe support en déplacement chez un client pour l'installation de notre logiciel sur plusieurs serveurs a appelé pour que nous cherchions la cause d'une erreur que notre client reproduisait facilement. L'équipe a mobilisé toute son énergie pour d'une part reproduire le problème, d'autre part trouver la solution. Pour l'anecdote, nous n'avons pas eu besoin de modifier notre logiciel car l'erreur provenait d'un champ de la base de données qui était mal dimensionné! La satisfaction collective était extraordinaire; quel plaisir de montrer à notre client que nous savons répondre très rapidement à ses problèmes; de plus, sortir un collègue d'un potentiel embarra renforce grandement le plaisir partagé!

La journée a continué avec la création d'un nouveau programme d'installation en pair-programming et s'est terminée sur l'automatisation de sa construction. Nous ne créons pas très souvent de nouveau programme de ce type alors le fait d'être en paire nous a permis de ne pas rester bloqué car les idées arrivent beaucoup plus vite à deux que seul.

La pratique du pair-programming n'est pas naturelle. Elle demande que les membres de l'équipe se fassent confiance car programmer en paire, c'est s'exposer. Il faut donc faire un petit effort au début puis très vite, on se rend compte qu'un travaille réalisé en paire permet d'obtenir des très bons résultats et pour un temps total probablement inférieure au temps qu'une même activité aurait demandé à une personne seule. On peut très vite préférer travailler à plusieurs plutôt que seul.

Le partage qui résulte du pair-programming a de multiples avantages. Il permet d'aquérir des compétences, d'avoir une bonne propriété collective du code, de trouver les meilleures solutions, d'avoir du fun en partageant les "micro-succès" et j'en oubli !

Le travail collaboratif est une très bonne étape. Le pair-programming demande encore plus de relations et d'interractions entre les membres d'une équipe; il permet de soutenir la première valeur du Manifeste Agile :
"L’interaction avec les personnes plutôt que les processus et les outils".


Share/Bookmark

samedi 2 août 2008

Un podcast "en français" sur le développement logiciel

Voici un site proposant des sujets d'actualité sur le developpement logiciel :
http://www.visualstudiotalkshow.com/
En cherchant dans les archives, j'ai eu le plaisir de réentendre Francois sur le podcast suivant:
----------------------------------------------------------------------------------------------
20 juin 2006 (Émission #0033)
Francois Beauregard: Les approches 'Agile' de développements de logiciels
Nous discutons avec Francois Beauregard des différentes approches de développements de logiciels dite 'Agile' permettant de faire des logiciels de meilleur qualité, à coût inférieur. Entre autres, nous allons discuter du manifeste Agile et des pratiques tel que SCRUM et XP ainsi que de de l’état du marché. Les approches Agile se distingue par un processus itératif qui appuie les équipes de développement afin qu'elles puissent livrer des fonctionnalités opérationnelles rapidement dans un processus de conception de logiciels rigoureux et efficace.
-----------------------------------------------------------------------------------------------

Share/Bookmark