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.