Zork[Yy]'s log

Aller au contenu | Aller au menu | Aller à la recherche

Agenda de novembre 2009

Quelques rendez-vous qui me semblent intéressants :

  • Valtech days 2009 le 17 novembre (payant), cette fois-ci, je n'y serai pas : 1 jour de moins pour plus cher (300 € HT), dommage car le programme me semblait intéressant.

Agenda d'octobre 2009

Quelques évènements :

  • les mercredis du développement reprennent, à l'exception près que celui-ci aura lieu un jeudi, le 1er octobre, la journée, sur WPF + SL,
  • After Work sur le thème de l'usine logicielle le 7 octobre à la Défense.
  • MS days 2009 rencontres techniques, le 6 & 7 octobre à Issy les Moulineaux, d'autres dates en octobre / novembre dans de grandes villes de France (Lille, Marseille, ...)
  • côté Alt.NET FR, le 12 octobre sera une soirée dédiée à MonoTouch (by Novell) où comment développer des applis. pour l'iPhone en C# / .NET...et non Objective-C, les informations et l'inscription sur le site Alt.NET FR

Tests in progress

Préambule

Nous sommes en train de reprendre un module pour préparer une bascule prochaine. Ce module concerne une gestion de listes de diffusion (de discussions, distributions, newsletters, ...) qui contient des actions simples : création ou suppression d'une liste, ajout ou suppression d'un abonnement.

C'est l'occasion d' implémenter des tests sur ce module. Pas tout à fait TDD dans le principe, mais cela aura au moins le mérite de commencer à prendre la main sur les frameworks et la méthode d'implémentation.

On va procéder par étapes, en commençant petit, il est toujours préférable d'être raisonnable dans ses objectifs, think big, start small disait mon grand-père agile.

Dans l'écriture de ces tests, il va s'agir de vérifier les entrées (chaînes de caractères) passées aux différentes méthodes, à savoir que le nom de la liste, et l'email de l'abonné soient bien formés (en gros que les regexp qui vérifient ces entrées fonctionnent).

On tentera (presque) de respecter le modèle du triptyque RED-GREEN-REFACTOR, sauf que dans notre cas, il existe du code :

  • écriture d'un test, le faire planter, le résultat du test runner doit être au rouge,
  • faire passer le test au vert,
  • réécrire le code le cas échéant

Outils

Le framework de tests utilisé est MbUnit, et le runner un add-in VStudio TestDriven.NET. Une version d'évaluation de ReSharper est également installée pour accélérer les ajustements de code.

Dès que je peux, je tenterai également ce projet qui me parait fort intéressant : mbunit-R#.

Mise en place

Un peu de théorie

Le module s'appelle ListManager, notre projet de tests se nommera donc selon la convention ListManager.Tests. Les noms des méthodes de tests sont écrites selon le format suivant : ''NomMethodManager_ResultatAttendu_ScenariodeTest'.

Les méthodes de notre Manager à tester sont de l'ordre de 4, on s'arrêtera à la 1ère pour notre exemple :

  • CreateList(list) : créer une liste
  • DeleteList(list) : supprimer une liste
  • Subscribe(list, email) : abonner à une liste un email
  • Unsubscribe(list, email) : désabonner d'une liste un email

Sur notre existant, chaque méthode ne renvoie que des exceptions, notamment sur la vérification de la validité des entrées, ici list et email. Des expressions régulières sont utilisées pour tester le format de ces 2 paramètres. En gros que les caractères du type [a-z0-9-_.] sont acceptés. Chaque méthode renvoie une exception ArgumentException s'il s'avère que le format des chaînes est erroné.

Le Manager peut être utilisé avec 2 fournisseurs possibles, qui représentent 2 moteurs de listes de diffusion (SYMPA ou LYRIS). L'un ou l'autre est sélectionné selon la stratégie adoptée. Les tests de différents cas d'entrées sont à dérouler bien entendu sur les 2 providers.

La base posée, passons à l'écriture de nos tests.

Passons à la pratique

Testons CreateList(). Le résultat attendu est une exception ArgumentException sur des chaînes qui ne respectent pas le format attendu. Le test doit être au vert si la méthode renvoie donc bien une exception sur une mauvaise entrée, par exemple :

CreateList("éléphant") doit renvoyer une erreur. Le code pour tester que ces cas renverront bien un exception pour les 2 providers, l'attribut [ExpectedArgumentException] de MbUnit permet de tester l'exception attendue :

  1. using ListManagerConnector;
  2. using MbUnit.Framework;
  3.  
  4. namespace ListManager.Tests
  5. {
  6. [TestFixture]
  7. public class ListManagerTests
  8. {
  9. [Test]
  10. [ExpectedArgumentException]
  11. public void CreateList_onSympa_ShouldThrow_IfNameOfList_IsNotWellFormed()
  12. {
  13. var lm = AbstractFactory.Factory("SYMPA").ListManagerControler;
  14. lm.CreateList("éléphant");
  15. }
  16.  
  17. [Test]
  18. [ExpectedArgumentException]
  19. public void CreateList_onLyris_ShouldThrow_IfNameOfList_IsNotWellFormed()
  20. {
  21. var lm = AbstractFactory.Factory("LYRIS").ListManagerControler;
  22. lm.CreateList("éléphant");
  23. }
  24. }
  25. }

Ce premier est pas mal, mais rien que pour éléphant, il nous faut 2 méthodes de tests. Pour chaque motif à tester, 2 méthodes supplémentaires, ce qui devient vite rébarbatif et donc peu motivant. Pour améliorer cela, MbUnit fournit la possibilité d'injecter des jeux de tests dans les méthodes (dans le même principe que des tests fonctionnels, voir FitNesse). Pour cela, utilisons les attributs [RowTest] (qui remplace l'attribut de base [Test]) et [Row] pour dérouler les cas intéressants et ce, sur les 2 providers.

Testons les motifs éléphant, l'éléphant, et certains autres cas sur les providers nommés SYMPA et LYRIS. Le paramètre strategy permet de tester chaque méthode sur les 2 providers :

  1. using ListManagerConnector;
  2. using MbUnit.Framework;
  3.  
  4. namespace ListManager.Tests
  5. {
  6. [TestFixture]
  7. public class ListManagerTests
  8. {
  9. [RowTest]
  10. [Row("malist@cc", "SYMPA")]
  11. [Row("malist@dummy.fr", "SYMPA")]
  12. [Row("", "SYMPA")]
  13. [Row("éléphant", "SYMPA")]
  14. [Row("l'elephant", "SYMPA")]
  15.  
  16. [Row("malist@lyris.fr", "LYRIS")]
  17. [Row("malist@fr", "LYRIS")]
  18. [Row("", "LYRIS")]
  19. [Row("éléphant", "LYRIS")]
  20. [Row("l'elephant", "LYRIS")]
  21. [ExpectedArgumentException]
  22. public void CreateList_ShouldThrow_IfNameOfList_IsNotWellFormed(string listname, string strategy)
  23. {
  24. var lm = AbstractFactory.Factory(strategy).ListManagerControler;
  25. lm.CreateList(listname);
  26. }
  27. }
  28. }

et voilà, comment en une méthode implémenter un jeu de 10 tests. Jouons la méthode avec TestDriven sous VStudio 2008 et le runner de MbUnit. On voit que MbUnit déroule chaque cas (éléphant, l'éléphant, ..) sur la même méthode.

Sous Visual Studio :

MbUnit

En lançant le runner de MbUnit :

MbUnit

Si nous avions introduit une donnée correcte, un des tests aurait été dans l'état rouge ou FAILURE, par exemple le test suivant doit échouer :

  1. [Row("od", "SYMPA")]

MbUnit

à chaque nouvelle écriture d'une méthode de test, on commencera par la faire échouer de cette façon.

Rétrospective

Ces premiers tests répondent à tout ou partie à notre besoin : vérifier que les entrées de nos méthodes sont bien formées.

Quelques retours sur ces 1ers tests :

  • dans notre cas, il a fallu que les 2 serveurs de listes soient en fonction, sans compter la configuration ad-hoc nécessaire. Aussi, il aurait été bénéfique que les 2 providers soient injectés dans le manager, afin de pouvoir les simuler lors des tests, part des Stubs / Mocks par exemple. Ici, on part du principe que nos serveurs fonctionnent et rendent le service attendu. Bien sûr, si l'objectif était de les tester alors on déploierait les tests nécessaires, même si cela concerne plus de l'intégration que des tests unitaires,
  • on comprend mieux la nécessité d'une usine d'intégration continue. Je m'imagine mal exécuter des centaines de tests à chaque fois sur mon IDE. L'usine peut apporter une solution à cette question.
  • j'ai pu m'apercevoir que des cas passaient alors qu'ils sont interdits, des bugs donc éventuels, maintenant corrigés.
  • au niveau de la conception objet, on s'aperçoit vite que c'est perfectible, que le code doit être non seulement OO mais aussi testable, ce qui implique un compromis des deux.
  • bonne expérience, à répéter de toute urgence, passionnant !

Tips & Tricks

Sous Visual Studio, à l'aide de TestDriven, le fait de se placer sur une méthode de test et de lancer les tests ne déroule ces derniers que sur la méthode.

ReSharper ou R# est un très très bon add-in, c'est assez remarquable ce qu'il arrive à conseiller comme réécriture du code, il reste à convaincre d'investir 200 € (par licence).

TDD & intégration continue

Nous avons eu la semaine dernière avec l'équipe une formation en intra de 4 jours sur 2 sujets très intéressants : le TDD et l'intégration continue. Cette formation avait pour objectif de nous mettre un pied à l'étrier sur la base du TDD et de l'intégration continue. Ces 2 modules demanderont certainement par la suite un investissement personnel non négligeable mais tellement positif et passionnant, en droite ligne avec les méthodes agiles (agile dans le sens XP).

Lire la suite...

Links #5 : POO et bonnes pratiques

- page 1 de 3