Ajax par la pratique

20 mars 2007 par Alexis KARTMANN

Je viens de terminer la lecture d’Ajax par la pratique, chez O’Reilly.

C’est le genre que bouquin que j’aime lire maintenant, parce que ce n’est pas qu’une collection de bout de code ou une copie de document de référence. Franchement avec un accès internet et vu le nombre de sites d’exemple et de documentation qui existe je ne comprends pas que l’on publie encore des bibles remplis de code source et de verbatim des documentations officielles.

A l’inverse, et contrairement à ce que son titre pourrait laisser croire, Ajax par la pratique est plutôt un guide général, et pas une suite de travaux pratique.

Après une présentation générale, on attaque direct avec une étude de cas sur Google Maps, avec création d’une solution utilisant les même principes. Evidement avec ce sujet, j’était particulièrement intéressé.

Ensuite les principes de base d’Ajax sont présentés, avec du code qui permet de mieux visualiser, et un fil rouge qui est l’intégration d’Ajax dans un site existant.

Les principaux Framework sont présentés (Json, Prototype, Script.aculo.us, Dojo, Atlas, DWR), ainsi que l’intégration avec les environnements de développement web serveur (Java, PHP, Rails, Asp.net).

C’est assez vivant, pas trop technique, mais quand même réservé à un public connaissant au moins html et javascript.

Bref je le conseille à tous ceux qui veulent se lancer dans Ajax et se demandent par où commencer.


Sortie de WPF/E sur Windows et Mac

4 décembre 2006 par Alexis KARTMANN

Microsoft vient de sortie la “technologie preview” de WPF/E pour Windows et Mac.

Vous avez bien lu, une version Mac sort en même temps que sur Windows. Et une version Linux devrait sortir aussi (mais réalisé par une société tierce, Microsoft n’étant pas prêt à sortir des produits sous Linux).

En regardant les demos, on pense tout de suite à Flash, bien sur. Avec comme principales différences le fait que le SDK n’est disponible que sous Windows, et aussi que la description des objets se fait en XML (en fait un sous-ensemble de XAML, la version Microsoft de XUL, disons).

Une technologie à creuser malgré tout, car elle sera bientôt présente sur beaucoup de machine, Vista oblige…


Souvenir d’eXtreme programming #14 : La responsabilité collective du code

12 octobre 2005 par Alexis KARTMANN

Après un longue pause, voici le retour du feuilleton sur la mise en place de l’eXtreme programming dans une dotcom. Et pour ce grand retour, quel meilleur sujet que la responsabilité collective du code ?

Rappelons que la responsabilité collective du code consiste, dans un projet, à donner à tous les membres de l’équipe le droit de modifié la totalité du code source. C’est donc contraire au principe souvent utilisé de responsabilité individuelle du code, dans lequel chaque développeur est responsable du code qu’il a écrit et reste le seul à avoir le droit et le devoir de le modifier.

La responsabilité collective du code s’est avérée, dans le cas qui nous occupe, l’une des pratiques qui semblait la plus naturelle (à l’opposé du travail en binôme, par exemple), mais dont la mise en place c’est le plus heurté à des barrières psychologiques.

Voyons lesquelles.

Je suis le meilleur

Il y a d’abord le caid, le dieu vivant du développement. Tellement bon et conscient de son niveau qu’il ne peut pas laisser les autres toucher à son code. Ou alors il faut qu’il refasse tout derrière.

Alors même s’il souffre d’être le seul à pouvoir toucher son code, il aura du mal à le partager. De la rétention de code, en somme.

Je ne suis pas digne

De l’autre côté, il y a le programmeur humble (très rare, mais il existe). Face au caid, il est impressionné. Tellement qu’il n’ose même pas imaginer qu’il pourrait faire la même chose.

Alors l’idée qu’il puisse toucher au code de son idole le paralyse.

Et comment on fait ?

Le binômage, le binômage. Tout est là. On mélange les niveaux, la connaissance du code se partage, le niveau technique général s’améliore.

Du moins en théorie. Ce fut même globalement le cas. Mais pas sans heurt.

L’euphorie

Après la phase initiale de méfiance, la propriété collective code écrit sur le nouveau projet s’instaura sans restriction. Ce ne fut pas le cas de l’existant tout de suite. Mais comme la majorité du travail était lié au nouveau projet ce ne fut pas un problème.

En fonction des scénarios utilisateur et des tâches de développeur, chaque binôme intervenait sur toute les parties du code sans autocensure ni blocage.

Mais on vit apparaitre un nouveau comportement chez les caïds, finalement assez voisin de la rétention de code.

Le remaniement des caïds

Un binôme réalise une tâche de développement. Par la suite un binôme de caïds doit travailler sur le code, et décide de faire un remaniement. En fait remaniement, c’est presque une réécriture qui est réalisée.

Quelque soit l’humilité du binôme, voir son code complètement réécrit est une sacrée remise en cause.

Le pire, c’est même qu’après un certain temps les caïds commencent à systématiquement remanier le code des autres binôme.

Voyant cela j’étais un peu pessimiste quand à la productivité de l’équipe. En effet quel intérêt de tout refaire systématiquement, autant tout faire faire par les caïds.

Montre moi et j’apprendrais

En fait le simple fait d’avoir écrit un code et les tests associés rendait quand même le travail de remaniement plus facile pour les caïds que s’il avait du tout faire eu même. Donc la perte de productivité n’était pas si évidente.

Et le remaniement des caïds eut finalement un effet pédagogique. Le fait de voir son code réécrit par les caïds permit aux autres binômes de mieux comprendre ce qu’il fallait faire. De plus le code existant étant souvent la base des nouveaux développements, le fait de laisser les caïds faire du remaniement tend à améliorer la qualité du code.

L’esprit d’équipe

Finalement si le remaniement des caïds n’a pas eu l’effet dévastateur que l’on pouvait craindre, c’est parce qu’il existait un vrai esprit d’équipe. Mais avant la responsabilité partagée du code, l’esprit était là, mais pas la pratique. Avec la responsabilité partagé, le code est vraiment devenue une œuvre collective, et non plus une collection d’œuvres individuelles.


Souvenir d’eXtreme programming #13 : Le remaniement constant (constant refactoring)

7 juillet 2005 par Alexis KARTMANN

Et non cher lecteur, le feuilleton eXtreme Programming n’est pas fini ! J’ai seulement eu d’autres projets quelque peu prenants (nDumbster entre autre).

Parlons donc du remaniement constant (constant refactoring). Il ne s’agit pas de déplacer régulièrement les personnes dans l’open space, même si cette pratique était aussi en vogue dans notre dotcom (on parlait de réorganisation spatiale !), mais de ne pas hésiter à modifier le code afin de le rendre plus propre, plus simple, plus maintenable.

Le remaniement est rendu possible par la présence de tests unitaires suffisants pour permettre des changements plus ou moins profonds dans le code sans risquer d’en perturber le fonctionnement (du moins une fois que tous les tests passent, bien sur). Sans une couverture complète du code par des tests unitaires, le remaniement constant devient une activité proche du saut à l’élastique sans élastique. Je parle de couverture complète, et non pas totale, pour signifier qu’il s’agit d’avoir voir suffisamment de tests pour tester “tout ce qui peut casser”.

Harnaché avec ses tests unitaires, le développeur peut donc se lancer dans les remaniements les plus audacieux, et cela sans le moindre risque. Enfin presque. Du moins nous avons constaté la fin de cette peur de perturber du code existant dont l’effet le plus visible chez nous était la tendance à dupliquer le code plutôt qu’a l’adapter à un nouveau besoin. Nous avions ainsi dans notre code ASP une pléthore de fonctions presque identiques à quelque variation près. Tout le monde avait peur de modifier une fonction existant en introduisant une variable supplémentaire afin d’éviter du code redondant, par exemple.

L’ouvrage

Refactoring de Martin Fowler, bien qu’illustré par des exemples en Java est très bien adapté à C# (quel hasard !). L’auteur y présente une liste de remaniement unitaire type qui permettent d’améliorer l’architecture d’une application.

Et c’est bien là ce que permet le remaniement constant. A partir de petites améliorations, mais que l’on fait constamment, on obtient une application dont l’architecture est bonne tout au long de sa vie.

C’est bien évidement à l’encontre de l’approche classique, dans laquelle un architecte définira en détail l’architecture de l’application à partir de son cahier des charges. Comme en XP le cahier des charges détaillé n’existe pas, l’architecture détaillé non plus. Disons plutôt qu’on la construit au fur et à mesure.

Ceci ne rend cependant pas inutile tout notion d’architecture, mais change seulement la façon dont on la construit. En fait en XP on construit quasiment en même temps le cahier des charges, l’architecture et le code !

Dans le contexte d’un logiciel dont la définition n’est pas figé, sur un marché changeant, avec des clients ayant du mal à exprimer explicitement leurs besoins, mais capable de formuler précisément des demandes d’évolution, eXtreme Programming s’est révélé efficace. Et le remaniement permet d’obtenir une architecture homogène et non un patchwork de fonctionnalités collés ensembles.

Parmi les effets néfastes du refactoring si l’on n’y prend pas garde, c’est qu’il devient l’excuse du perfectionniste en retard. “D’accord j’ai passé trois jours sur cette tache prévue pour un jour, mais j’ai fait deux jours de refactoring”. Dans ce cas, je rappelle que le remaniement doit être constant, c’est à dire répartie durant les taches de développement, et non pas considéré comme une tache indépendante. On ne fait pas du remaniement sur n’importe quelle partie du code, mais sur une partie de code que l’on doit modifier pour une tâche en cours.

Il y a une bonne comparaison (je crois qu’elle vient de Kent Beck) entre le remaniement et le fait de nettoyer sa cuisine. Il vaut mieux cuisiner dans une cuisine propre (donc développer avec un code propre), mais on peut soit nettoyer sa cuisine avant de cuisiner, soit après avoir cuisiné, soit pendant qu’on cuisine. Et évidement si on nettoie pendant qu’on cuisine, le temps que l’on passe dans la cuisine n’est pas tellement plus long, alors que nettoyer avant de cuisiner risque de prendre plus de temps, surtout les casseroles bien sales ! Bref c’est lorsque l’on remanie son code durant l’écriture que le remaniement est le moins couteux.

Il reste encore quelques pratiques d’eXtreme Programming à traiter avant la fin de ce feuilleton. Si vous avez des questions ou des points que vous voudriez voir traiter, n’hésitez pas. Amis eXtreme Programmers, ne soyez pas triste à l’annonce de la fin proche, j’ai encore d’autres expériences à raconter. Notamment de l’eXtreme Programming en solo !

A suivre…


Comment tester une classe ou une méthode privée ou internal en dotNet ?

5 juillet 2005 par Alexis KARTMANN

Lorsque l’on fait du développement conduit par les tests (Test Driven Development), on écrit les tests avant d’écrire son code. Ce qui entraine naturellement d’écrire du code testable. Et l’on a donc naturellement tendance à rendre public tout ce qui doit être testable (classes ou méthodes).

Jusqu’ici je n’avais pas vraiment rencontré de cas où ce principe était en défaut. Mais c’était surtout parce que j’écrivais des applications complètes et non des librairies. Puis j’ai travaillé sur nDumbster.

nDumbster avait été écrit en TDD, donc toutes les classes et méthodes testé étaient publique. Et certaines classes uniquement nécessaires au fonctionnement de nDumbster avaient été déclaré publiques pour cette raison. Mais elles ne servaient qu’à modéliser le protocole SMTP et n’avaient pas à être connus du développeur utilisant nDumbster pour tester son application.

Ceci ne me posait pas de problème, jusqu’à ce que j’utilise nDoc pour générer la documentation des classes de nDumbster. Et évidement toutes les classes et les méthodes apparaissaient dans la documentation, alors que 2 classes sont réellement utiles. D’où une documentation plus difficile à comprendre.

J’ai d’abord pensé à utiliser des tags spécifiques pour ne documenter que les classes et les méthodes utiles, ce qui n’est pas la bonne solution. D’une part parce qu’il peut être utile d’avoir une documentation complète de toutes les classes, à destination des développeurs de la librairie, ce qu’il est facile de faire en activant ou non l’option de nDoc de documentation des classes et méthode privées et internes. Et d’autre part parce que les utilisateurs de la librairie ne devraient pas pouvoir utiliser les classes et les méthodes d’implémentations (sinon à quoi servirait la notion de portée en Csharp ?).

Donc il fallait rendre internal les classes et les méthodes que l’utilisateur de la librairie n’avait pas à connaitre. Cela étant dit, il reste le problème des tests unitaires. Comment tester une classe internal ?

Une première solution est d’écrire les tests dans la même assembly que les classes privées. C’est possible car NUnit peut lire n’importe quelle assembly. Le problème c’est de livrer un assembly contenant des tests et lié aux assembly de nUnit. C’est vrai que nDumbster est censé être utilisé pour faire des tests, mais pas nécessairement avec NUnit ! Si on part dans cette voie, la solution est d’entourer la définition des classes de test par des conditions de compilation, par exemple #if test. Il vaut mieux utiliser une autre définition que DEBUG, pour pouvoir tester les deux versions debug et release. Et dans ce cas si aucune utilisation des classes de nunit n’est faite, la référence n’est pas prise en compte par le compilateur, ce qui élimine la dépendance dans les versions non testables. Mais cela ne permet pas de tester les méthodes privées ou protégées.

Le problème de cette approche est que dans l’utilisation de directives de compilation a tendance à perturber les IDE et les outils comme ReSharper. De plus mélanger le code utile et le code de test empêche de calculer facilement un ratio code utile/code de test. Et bien sur nDumbter le code utile et les tests était déjà dans deux assembly différents. Il me fallait une autre solution.

Sous l’influence de Rédo depuis notre rencontre, je me dis qu’avec System.Reflection je devrais trouver mon bonheur.

Je cherche un peu sur le web, et je trouve le blog de Steven M. Cohn qui propose une classe répondant presque à mon besoin. Je vous livre ici la version modifiée par mes soins, afin de pouvoir créer des objets public pour utiliser leur méthodes protégée ou internes, et de pouvoir appeler les méthodes privée/internes d’un objet dont on a une référence (par exemple le résultat d’une méthode d’une autre classe).

Le constructeur de la classe PrivateClassTester utilise comme premier paramètre le nom complet du type d’objet à créer (utilisé par Type.GetType()). Les arguments suivants sont les paramètres du constructeur à utiliser pour l’objet à tester. Ils détermine la signature du constructeur qui sera utilisé.

Le nom complet sera de la forme MonEspaceDeNom.MaClasse,MonNomDAssembly.

Un autre constructeur accepte simplement l’instance d’un objet dont on veut appeler les méthodes privées.

Voici le code de la classe PrivateObjectTester :

CSharp [Show Plain Code]:
  1. using System;
  2.  
  3. using System.Reflection;
  4.  
  5. using System.Security.Permissions;
  6.  
  7.  
  8.  
  9.  
  10.  
  11. #region .
  12.  
  13. /// <summary>
  14.  
  15. /// This utility class uses reflection to wrap an instance of a
  16.  
  17. /// class to gain access to non-public members of that class.
  18.  
  19. /// This is an internal utility class used for unit testing.
  20.  
  21. ///
  22.  
  23. /// Based on a class by Steven M. Cohn
  24.  
  25. /// http://weblogs.asp.net/stevencohn/archive/2004/06/08/151235.aspx
  26.  
  27. /// </summary>
  28.  
  29. #endregion
  30.  
  31. public class PrivateObjectTester
  32.  
  33. {
  34.  
  35.         private const BindingFlags bindingFlags =
  36.  
  37.                    BindingFlags.Instance | BindingFlags.Public |
  38.  
  39.                    BindingFlags.NonPublic | BindingFlags.Static;
  40.  
  41.         private Type type;                  // type of class to manage
  42.  
  43.         private object instance;            // managed instance of type
  44.  
  45.         private ReflectionPermission perm;  // for non-public members
  46.  
  47.         #region .
  48.  
  49.         /// <summary>
  50.  
  51.         /// Initializes a new instance wrapping a new instance of the
  52.  
  53.         /// target type. One PrivateObject manages exactly one instance
  54.  
  55.         /// of a target type.
  56.  
  57.         /// </summary>
  58.  
  59.         /// <param name="qualifiedTypeName">The qualified name of the type.
  60.  
  61.         /// This should include the full assembly qualified name including
  62.  
  63.         /// the namespace, for example "MyNamespace.MyType,MyAssemblyBaseName"
  64.  
  65.         /// where MyNamespace is the dotted-notation namespace, MyType is the
  66.  
  67.         /// name of the type and MyAssemblyBaseName is the base name of the
  68.  
  69.         /// assembly containing the type.
  70.  
  71.         /// </param>
  72.  
  73.         /// <param name="args">An optional array of parameters to pass to
  74.  
  75.         /// the constructor. If this argument is not specified then the
  76.  
  77.         /// default constructor is used. Otherwise, a constructor that
  78.  
  79.         /// matches the number and type of parameters is used.
  80.  
  81.         /// </param>
  82.  
  83.         #endregion
  84.  
  85.         public PrivateObjectTester (string qualifiedTypeName, params object[] args)
  86.  
  87.         {
  88.  
  89.                 perm = new ReflectionPermission(PermissionState.Unrestricted);
  90.  
  91.                 perm.Demand();
  92.  
  93.  
  94.  
  95.                 type = Type.GetType(qualifiedTypeName);
  96.  
  97.  
  98.  
  99.                 Type[] types = new Type[args.Length];
  100.  
  101.                 for (int i=0; i < args.Length; i++)
  102.  
  103.                 {
  104.  
  105.                         types[i] = args[i].GetType();
  106.  
  107.                 }
  108.  
  109.  
  110.  
  111.                 ConstructorInfo constructor =   
  112.  
  113.                             type.GetConstructor(bindingFlags,null,types,null);
  114.  
  115.                 instance =constructor.Invoke(args);
  116.  
  117.         }
  118.  
  119.  
  120.  
  121.         public PrivateObjectTester (object instance)
  122.  
  123.         {
  124.  
  125.                 perm = new ReflectionPermission(PermissionState.Unrestricted);
  126.  
  127.                 perm.Demand();
  128.  
  129.  
  130.  
  131.                 type = Type.GetTypeFromHandle(Type.GetTypeHandle(instance));
  132.  
  133.  
  134.  
  135.                 this.instance = instance;
  136.  
  137.         }
  138.  
  139.  
  140.  
  141.         #region .
  142.  
  143.         /// <summary>
  144.  
  145.         /// Gets the instance of the managed object.
  146.  
  147.         /// </summary>
  148.  
  149.         #endregion
  150.  
  151.         public object Instance
  152.  
  153.         {
  154.  
  155.                 get { return instance; }
  156.  
  157.         }
  158.  
  159.  
  160.  
  161.  
  162.  
  163.  
  164.  
  165.         #region .
  166.  
  167.         /// <summary>
  168.  
  169.         /// Gets the value of a non-public field (member variable) of the
  170.  
  171.         /// managed type.
  172.  
  173.         /// </summary>
  174.  
  175.         /// <param name="name">The name of the non-public field to
  176.  
  177.         /// interrogate</param>
  178.  
  179.         /// <returns>A value whose type is specific to the field.</returns>
  180.  
  181.         #endregion
  182.  
  183.         public object GetField (string name)
  184.  
  185.         {
  186.  
  187.                 FieldInfo fi = type.GetField(name, bindingFlags);
  188.  
  189.                 return fi.GetValue(instance);
  190.  
  191.         }
  192.  
  193.  
  194.  
  195.         #region .
  196.  
  197.         /// <summary>
  198.  
  199.         /// Gets the value of a non-public property of the managed type.
  200.  
  201.         /// </summary>
  202.  
  203.         /// <param name="name">The name of the non-public property to
  204.  
  205.         /// interrogate.</param>
  206.  
  207.         /// <returns>A value whose type is specific to the property.</returns>
  208.  
  209.         #endregion
  210.  
  211.         public object GetProperty (string name)
  212.  
  213.         {
  214.  
  215.                 PropertyInfo pi = type.GetProperty(name,bindingFlags);
  216.  
  217.                 return pi.GetValue(instance,null);
  218.  
  219.         }
  220.  
  221.  
  222.  
  223.  
  224.  
  225.  
  226.  
  227.         #region .
  228.  
  229.         /// <summary>
  230.  
  231.         /// Invokes the non-public method of the managed type.
  232.  
  233.         /// </summary>
  234.  
  235.         /// <param name="name">The name of the non-public method to invoke.</param>
  236.  
  237.         /// <param name="args">And optional array of typed parameters to pass to the
  238.  
  239.         /// method.  If this argument is not specified then the routine searches
  240.  
  241.         /// for a method with a signature that contains not parameters.  Otherwise,
  242.  
  243.         /// the procedure searches for a method with the number and type of parameters
  244.  
  245.         /// specified.
  246.  
  247.         /// </param>
  248.  
  249.         /// <returns>A value who type is specific to the invoked method.</returns>
  250.  
  251.         #endregion
  252.  
  253.         public object Invoke (string name, params object[] args)
  254.  
  255.         {
  256.  
  257.                 Type[] types = new Type[args.Length];
  258.  
  259.                 for (int i=0; i < args.Length; i++)
  260.  
  261.                 {
  262.  
  263.                         types[i] = args[i].GetType();
  264.  
  265.                 }
  266.  
  267.  
  268.  
  269.                 return type.GetMethod(name,bindingFlags,null,types,null).Invoke(instance,args);
  270.  
  271.         }
  272.  
  273.  
  274.  
  275.  
  276.  
  277.  
  278.  
  279.         #region .
  280.  
  281.         /// <summary>
  282.  
  283.         /// Sets the value of a non-public field (member variable) of the managed type.
  284.  
  285.         /// </summary>
  286.  
  287.         /// <param name="name">The name of the non-public field to modify.</param>
  288.  
  289.         /// <param name="val">A value whose type is specific to the field.</param>
  290.  
  291.         #endregion
  292.  
  293.         public void SetField (string name, object val)
  294.  
  295.         {
  296.  
  297.                 type.GetField(name,bindingFlags).SetValue(instance,val);
  298.  
  299.         }
  300.  
  301.  
  302.  
  303.  
  304.  
  305.  
  306.  
  307.         #region .
  308.  
  309.         /// <summary>
  310.  
  311.         /// Sets the value of a non-public property of the managed type.
  312.  
  313.         /// </summary>
  314.  
  315.         /// <param name="name">The name of the non-public property to modify.</param>
  316.  
  317.         /// <param name="val">A value whose type is specific to the property.</param>
  318.  
  319.         #endregion
  320.  
  321.         public void SetProperty (string name, object val)
  322.  
  323.         {
  324.  
  325.                 type.GetProperty(name,bindingFlags).SetValue(instance,val,null);
  326.  
  327.         }
  328.  
  329. }

Je répète donc les mises en garde d’usage, n’utiliser cette classe que pour écrire les tests unitaires de bibliothèques. Dans un contexte d’application, son utilisation révèle à coup sur un problème de conception, que ce soit des tests ou des classes testés !

A titre d’exemple, le code suivant (qui n’est compilable que dans l’assembly nDumbster, les classes étant internal :

CSharp [Show Plain Code]:
  1. nDumbster.smtp.SmtpRequest request = new nDumbster.smtp.SmtpRequest(nDumbster.smtp.SmtpActionType.UNRECOG,
  2.  
  3.                                                                     null, snDumbster.smtp.SmtpState.CONNECT);
  4.  
  5. nDumbster.smtp.SmtpResponse  response = request.Execute();
  6.  
  7. Assert.AreEqual(500,  response.Code);

Devient :

CSharp [Show Plain Code]:
  1. PrivateObjectTester smtpAction =
  2.  
  3.      new PrivateObjectTester("nDumbster.smtp.SmtpActionType,nDumbster", (sbyte)0);
  4.  
  5. PrivateObjectTester smtpState =
  6.  
  7.      new PrivateObjectTester("nDumbster.smtp.SmtpState,nDumbster", (sbyte)0);
  8.  
  9. PrivateObjectTester request =
  10.  
  11.      new PrivateObjectTester("nDumbster.smtp.SmtpRequest,nDumbster",
  12.  
  13.         smtpAction.GetField("UNRECOG"), "",
  14.  
  15.         smtpState.GetField("CONNECT"));
  16.  
  17. PrivateObjectTester response = new PrivateObjectTester(request.Invoke("Execute"));
  18.  
  19. Assert.AreEqual(500,  response.GetProperty("Code"));

Bons tests !


Nouvelle version de nDumbster

4 juillet 2005 par Alexis KARTMANN

Je suis heureux d’annoncer la sortie d’une nouvelle version de nDumbster. nDumbster permet aux développeurs .Net d’écrire des tests unitaires d’application envoyant des e-mails en simulant un serveur SMTP.

Doté d’une licence Apache 2.0, il peut donc être utilisé par tout projet, open-source ou propriétaire.

nDumbster est au départ une adaptation de ndumbster (Java) en C#. C’est le résultat d’une collaboration entre Martin Woodward et moi-même.

Cette nouvelle version est la première réellement utilisable. Elle est fournit avec une documentation, et est compatible avec les plateformes suivantes :

  • .Net 1.0
  • .Net 1.1
  • .Net 2.0 Beta 2
  • Mono 1.0 (et 1.1)
  • Shared Source CLI 1.0
  • CLI 1.0 (c’est à dire une version release compatible avec toutes ces plateformes).

nDumbster est disponible sur http://ndumbster.sourceforge.net.


Tester un controle Calendar avec NUnitAsp.

26 juin 2005 par Alexis KARTMANN

NunitAsp est un outil fabuleux pour le test unitaire de site réalisé en ASP.Net

Surtout qu’avec l’utilisation d’un parser sgml il n’est plus nécessaire d’avoir un code XHTML pour l’utiliser.

Je m’en sert depuis une semaine de manière intensive et j’ai pu tout tester dans mon site grâce au testeurs de contrôle de base.

Tout, sauf le contrôle calendrier (Calendar) qui n’est pas géré en standard. Qu’à cela ne tienne, je regarde un peu le code généré, et il est assez facile de déterminer que le choix d’une date se fait par un post-back du type __doPostBack('identifiant du contrôle','identifiant du jour').

L’identifiant du jour me semble incrémental, mais sa valeur n’est pas triviale. Alors je lance Reflector et je regarde un peu le code de génération du contrôle calendrier. J’y voie que l’identifiant du jour est en fait le nombre de jour écoulé depuis le premier janvier 2000.

A partir de là, l’écriture d’un contrôle de test est assez facile :

CSharp [Show Plain Code]:
  1. using System;
  2.  
  3. using NUnit.Extensions.Asp;
  4.  
  5. using NUnit.Extensions.Asp.AspTester;
  6.  
  7.  
  8.  
  9. namespace NUnit.Extensions.Asp.MyTesters
  10.  
  11. {
  12.  
  13.         ///
  14.  
  15.         /// Test for for Calendar control.
  16.  
  17.         ///
  18.  
  19.         public class CalendarTester  : AspControlTester
  20.  
  21.         {
  22.  
  23.                 static private DateTime baseDate;
  24.  
  25.  
  26.  
  27.                 static CalendarTester()
  28.  
  29.                 {
  30.  
  31.                         baseDate = new DateTime(2000, 1, 1);
  32.  
  33.                 }
  34.  
  35.  
  36.  
  37.                 public CalendarTester(string aspId, Tester container) : base(aspId, container)
  38.  
  39.                 {
  40.  
  41.                 }
  42.  
  43.  
  44.  
  45.                 public void SelectDate(DateTime date)
  46.  
  47.                 {
  48.  
  49.                         TimeSpan daysSpan = date.Subtract(CalendarTester.baseDate);
  50.  
  51.                         int datecode = daysSpan.Days;
  52.  
  53.  
  54.  
  55.                         string tag =
  56.  
  57.                                            string.Format("javascript:__doPostBack(’{0}’,'{1}’)",
  58.  
  59.                                                   this.AspId, datecode);
  60.  
  61.                         PostBack(tag)
  62.  
  63.                 }
  64.  
  65.         }
  66.  
  67. }

Evidement ce code est limité, il ne permet pas de simuler la navigation d’un mois à l’autre, ni de vérifier que le jour choisi est bien affiché dans le calendrier, notamment si on surcharge l’événement DayRender du contrôle Calendar. Et il ne fonctionne qu’avec une sélection d’un jour unique.

Mais c’est un début, et cela devrait suffire dans la plupart des cas.


Souvenir d’eXtreme programming #12 : L’intégration continue

15 juin 2005 par Alexis KARTMANN

Continuons notre série en parlant un peu d’une pratique qui n’est pas propre à XP, mais tout processus de développement discipliné : l’intégration continue.

Le but est d’éviter un phénomène classique de rétention de source : Un développeur garde du code sur son poste plusieurs jours (semaines/mois ?) sans le réintégrer dans le système de gestion de source. Lorsqu’il effectue la réintégration, il peut passer un long moment à régler les conflits avec d’autres modifications effectué. Et dans certains cas, une réintégration un peu brutale peut provoquer d’autre problèmes (le fameux effet papillon).

Pour éviter cela, l’intégration continue consiste à réintégrer très régulièrement les sources dès que le développeur a atteint un état stable de l’application (c’est à dire dès que ses tests passent).

Un élément nécessaire pour l’intégration continue est l’utilisation d’un outil de gestion de version comme CVS ou Subversion. Dans notre cas, vu l’utilisation de Visual Studio, nous avons naturellement utilisé Visual Source Safe (VSS). VSS était configuré en mode verrou partagé. Cela signifie que plusieurs développeurs peuvent travailler sur le même fichier (C’est un peu comparable au mode de fonctionnement de CVS). C’est lors de la réintégration que l’on gère les conflits éventuels.

Lors de la présentation de l’organisation spatiale, j’avais parlé de la machine de build. Lorsqu’un binôme a terminé une tâche élémentaire, et que tous les tests passent sur sa machine, les sources sont réintégrés dans VSS, puis le binôme se rends devant sur la machine de build, récupère tous les sources de VSS (et pas seulement ceux qu’ils ont modifiés), recompile l’application et lance les tests. Si un problème se produit, ils doivent le corriger avant de retourner à leurs poste (ou de rentrer chez eux).

Lorsque les tests se passent bien sur la machine de build, les binaires sont copié dans un répertoire de livraison et installé sur le serveur de pré-production, avant d’éventuellement mis en production si l’équipe marketing valide cette version. Au départ afin de signaler qu’une livraison a été faite, on utilisait une crécelle, mais le bruit était un peu trop fort et son utilisation n’a pas duré.

Après une réintégration réussie, le binôme peut récupérer sur son poste de travail les dernières versions de code sur VSS, puis démarrer une nouvelle tâche. Et systématiquement avant de commencer à travailler le matin on récupère les dernières versions.

L’utilisation de la machine de build s’est vite généralisé, et finalement peu de problème de conflit ont eu lieu, ce qui fut un grand changement par rapport au passé (alors même qu’il y avait peu de code dont la responsabilité était partagée). Aujourd’hui la machine de build serait sans doute remplacé par un serveur de construction automatique comme CruiseControl.Net.

Malgré cela, l’intégration continue, couplée avec les tests unitaires, est la clé du succès de la responsabilité partagé du code. On en reparlera.

A suivre


Souvenir d’eXtreme programming #11 : Les règles de codage.

13 juin 2005 par Alexis KARTMANN

Après avoir traité de pratique de gestions de projet, revenons aux pratiques de programmation avec les règles de codage.

Ces règles furent discutées lors d’une réunion de l’équipe, en suivant les principes de simplicité et la contrainte suivante : moins de 10 pages pour les règles complètes, et la possibilité d’une version compacte tenant sur 2 pages que l’on placerait sur chaque poste de développement. L’expérience montrant que les règles trop complexes et nombreuses ne sont jamais suivit, ces contraintes sur la taille réduite en rendaient l’application plus probable. Il est vrai qu’aujourd’hui de nombreux outils permettent de valider automatiquement le respect de règles lors de la compilation ou avant la réintégration de fichier dans le système de gestion de source, mais nous devions faire avec les moyens du bord, à savoir l’équipe (ce qui est d’ailleurs conforme à l’esprit XP, qui privilégie les hommes plutôt que les outils).

A l’issue de la réunion, un document récapitulatif a été rédigé, ainsi qu’un guide synthétique de 2 pages. Placé dans un protège document transparent, ce guide sera utilisé en référence par tous les développeurs.

Mais voyons plus en détails les règles issues de la réunion.

Style de codage

Ce terme désigne la manière de présenter le code dans un but de faciliter la compréhension. Il comprend les méthodes d’indentation et de déclaration de variables.

Indentation

On choisi d’utiliser le style dit « côte ouest », par opposition à K&R. C’est-à-dire le style supporté par défaut par Visual studio. Et nous utilisions une tabulation de 2 espaces configurée dans l’éditeur (plutôt qu’avec le caractère tabulation) afin d’avoir une présentation identique sur tous les postes.

Ce qui donne quelque chose comme ceci :

CSharp [Show Plain Code]:
  1. try
  2.  
  3. {
  4.  
  5.   line = unputStream.ReadLine();
  6.  
  7. }
  8.  
  9. catch (Exception e)
  10.  
  11. {
  12.  
  13.   throw e;
  14.  
  15. }
Longueur des lignes et césure

Plutôt que de définir une stratégie de césure des lignes trop longues, nous décidons de remanier systématiquement ces lignes.

Ainsi

CSharp [Show Plain Code]:
  1. if (val = 52 && val1 = 5 || (val1 = 3 && val2 = 1))

Sera remplacé par :

CSharp [Show Plain Code]:
  1. if ( CheckValues() )

De même une fonction possédant une grande collection de paramètre sera traité en regroupant les paramètres dans une classe. Naturellement ce principe ne s’applique que pour des fonctions ayant une grande collection de paramètre. Il s’agit d’une certaine façon d’un remaniement qui consiste à regrouper des paramètres liés entre eux dans une structure ayant un sens, le nombre de paramètre élevé étant un indicateur (code smell) de ce remaniement nécessaire.

CSharp [Show Plain Code]:
  1. public String formatMessage(string message, int val1, int val2,
  2.  
  3.                      int val3);
  4.  
  5. public String formatMessage(formatMessage, ValuesBag vals);
Déclaration des variables

Les noms des variables devront avoir une signification claire. De plus la déclaration d’une variable devra se faire au début du bloc de code l’utilisant. Ceci est un bon compromis entre la lisibilité (déclaration des variables regroupé) et la performance (il faut éviter de déclarer des variables qui pourrait ne pas être utilisé).

Conventions de nommages

Il s’agit ici de définir la manière dont on nommera un élément en fonction de son type ou son usage. Il existe de nombreuses conventions, notamment la notation hongroise qui consiste à utiliser un préfixe définissant le type d’une variable. Cette notation peut vite devenir lourde à gérer, et surtout n’est plus vraiment adapté à des langages objet, puisque la plupart des variables seront d’un type non prévue par la notation. De plus avec les éditeurs modernes on trouve très facilement le type d’une variable si nécessaire.

Notre convention portera donc surtout sur la capitalisation des noms en fonction de l’objet nommé.

Classes et interface

Le nom commencera par une majuscule, et chaque mot accolé au premier commencera aussi par une majuscule.

Une classe n’aura pas de préfixe particulier, alors qu’une interface commencera par la lettre I.

On essayera de ne pas utiliser d’abréviation, sauf pour des termes très répandu.

Par exemple :

CSharp [Show Plain Code]:
  1. MyClass
  2.  
  3. IMailManager
  4.  
  5. MailManagerSMTP
Méthodes

Comme pour une classe, le nom commencera par une majuscule, et chaque mot accolé au premier commencera aussi par une majuscule.

On utilisera plutôt un verbe indiquant l’action que la méthode effectue.

Pour faciliter la compréhension, on inclura dans le nom des paramètres une information sur l’unité utilisé si nécessaire.

Par exemple :

CSharp [Show Plain Code]:
  1. WaitFor(int seconds) ;

Est plus clair que :

CSharp [Show Plain Code]:
  1. Wait(int delay) ;
Variables membres d’une classe et variables globales

On utilisera une minuscule pour le premier mot, et une majuscule sur les mots suivant.

Variable locale et paramètre d’une méthode

Le nom sera entièrement en minuscule.

Constantes et Macro C

Le nom sera entièrement en majuscule. On séparera les noms par un caret : _.

Enumérations

Les noms des énumérations utiliseront la règle s’appliquant aux classes, et les valeurs celle des constantes.

Documentation

L’un des principes de XP est que la documentation est dans le code. Plutôt que d’avoir un code obscur nécessitant une documentation pour le comprendre, on s’appliquera à obtenir un code simple et clair. Par contre dans le cas ou une documentation est nécessaire, notamment pour les APIs, on utilisera un outil de génération de documentation (comme XMLDoc pour C# ou doxygen pour C++) pour maintenir la documentation dans le code source. Cette pratique limite fortement le risque de divergence entre la documentation et la réalité du code.

Bilan

Même si les règles n’étaient pas toujours respectées, le fait d’avoir un consensus sur l’objectif s’est avéré très positif sur l’évolution du code. On a ainsi évité les discussions lié à la responsabilité collective du code sans que des règles claires soit définis (les fameux : « qui a foiré mon indentation ». et autres joyeusetés). On en reparlera bientôt en traitant justement de la responsabilité collective du code.

A suivre.


Souvenir d’eXtreme programming #10 : Le planning game

1 juin 2005 par Alexis KARTMANN

Pour la suite de notre feuilleton traitant de la mise en place d’eXtreme Programming, nous allons traiter du planning game.

Un changement dans la gestion des plannings

Avant la mise en place de XP, nous avions des réunions roadmap (plan de développement en français) stratégiques tous les trimestre et tactiques toutes les semaines.

La réunion tactique a été remplacée par les standup meetings matinaux.

La réunion trimestrielle a été remplacée par le planning game. Mais sa fréquence n’était plus fixe, mais fonction des besoins.

Quand ?

Nous avons d’abord fait un planning game initial, ou macroscopique, afin de définir les fonctions à développer d’une manière générales et faire un chiffrage approximatif.

Par la suite à chaque grande étape nous faisions un planning game pour définir le contenu de la prochaine release en détail.

Nous pouvions aussi décider lors d’un standup meeting de programmer un nouveau planning game si le besoin s’en faisait sentir, par exemple si suite à l’arrivée de beaucoup de demandes non prévues le planning devait être remis en cause.

Chiffrage

Le planning game se déroulait dans une salle fermée pour éviter toute distraction.

Dans un premier temps l’ensemble des scénarios utilisateur (ou fonctions), noté sur des fiches bristol, était chiffré collectivement par les développeurs, en fonction du coût de scénarios équivalents déjà réalisés. Au début nous étions forcement peu efficace sur les chiffrages, mais une fois que l’on avait à disposition une banque de fiches de scénario, les chiffrages étaient plus faciles, en procédant par analogie : « Ce scénario ressemble à tel autre qui nous a coûté tant, donc nous utiliseront le coût du réalisé pour le futur scénario ».

Planification

Une fois tous les scénarios chiffrés, le client classé les scénarios par priorité client ou marché. Parallèlement les développeurs classaient les scénarios par risque (facile, moyen, risqué). Le risque est différent du coût, en ce sens qu’il indique la difficulté technique, ce qui n’a rien à voir avec la durée. L’idée est de ne pas mettre trop de scénario risqué dans une même release pour d’une certaine manière repartir le risque entre les itérations.

Ensuite on calcule le coût réalisable par itération (en fonction des dates de release visé, et des effectifs disponible), et on repartie les scénario sur les releases à venir. On peut même planifier l’ensemble des fonctions voulues.

Et on itère

Evidement à partir de la première itération les plannings prévus n’ont jamais été respectés. Les raisons sont multiples et assumées : changement de priorité, ajout de nouveau scénario, problèmes techniques.

Chaque planning game permettait d’affiner le contenu le la prochaine release, tout en gardant une vision des futures releases. Le fait de ne plus avoir deux planifications (stratégique et tactique) réalisés séparément a permit d’avoir une vision plus correcte de ce qui sera disponible à une date donnée.

Bilan

Globalement on a obtenu une planification plus réaliste.

Par contre la durée du projet a largement dépassé le planning initial prévu, surtout parce que le niveau des fonctionnalités livrées a largement dépassé ce qui été prévu au départ.

A suivre



Propulsé par WordPress