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…


Visual Studio 2005 et SQL Server 2005 : De bon produits, un lancement raté.

9 novembre 2005 par Alexis KARTMANN

Hier j’ai assisté au lancement officiel de Visual Studio 2005 et SQL Server 2005.

Si avec ces versions la plateforme de développement de Microsoft devient à mon avis la meilleure plateforme du marché, c’est à l’un des plus mauvais lancement de produit auquel j’ai assisté hier :

  • Intervenants hésitants, même dans les vidéo ! A l’exception notable du PDG Eric Boustouller, très à l’aise.
  • Private jokes devant 2000 personnes.
  • Discours commercial creux.

Heureusement qu’il y avait les démo technique avec les toujours excellents Pascal Belaud et Eric Mittelette, sinon je serais parti avant la fin ! Un exception cependant,

Alors messieurs, comme vous dites, laissez la chance au produit, et arretez de jouer aux super-vendeurs.


De retour de DNG 2005

24 octobre 2005 par Alexis KARTMANN

Je rentre de DNG 2005.

Evidement la majorité des 600 participants étaient là pour voir Bill Gates, mais en attendant son arrivée à 17 heure, on a pu suivre plusieurs sessions de très bonne tenue.

Tout d’abord Eric Groise et Jean-Louis Bénard ont traité le sujet à la mode en ce moment avec la sortie de Team System, à savoir l’industrialisation du développement logiciel et la software factory. Une bonne présentation, un peu redondante par rapport aux DevDays 2005, mais avec moins de langue de bois.

Puis Sami JABER nous a ensuite présenté une vision du développement n-tier autour de l’Entreprise Service Bus. Un vaste sujet, traité avec beaucoup de pragmatisme et une double vision aujourd’hui/demain.

Pour ceux qui n’ont pas suivi la PDC, Guillaume Renaud, Thomas Gil et Sébastien Ros ont fait un point sur les nouveautés présentées, notamment C# 3.0, Linq, Avalon et Windows Workflow Foundation. Très intéressant, dommage que la certains technologies présentés ne sont pas disponibles avant des années.

Après le déjeuner, Didier Girard et Julien Brunet ont présenté une vision du client riche de demain, le bureau métier. Alors, les applications composites seront-elles le buzz-word de 2006, après AJAX en 2005 ?

Avant l’arrivée de Bill, Jean-Marc Prieur a présenté une mise en œuvre de Domain Specific Language avec les DSL Tools pour la marine nationale. La majorité de la salle a quand même décroché, malgré l’humour du présentateur.

Puis vient le moment d’accueillir l’architecte en chef de Microsoft. Evidement les questions étaient surement préparés, mais la maitrise du bonhomme est quand même impressionnante.

Nous n’avons bien sûr eut droit à aucune révélation, sauf peut-être quand Bill a avoué qu’il n’avait pas encore réussit à concevoir le PC dont il rêvait avec Paul Allen il y a 30 ans.

Un grand moment.


Installation de Visual Studio 2005 Release Candidate

5 octobre 2005 par Alexis KARTMANN

J’avais installé VS2005 Beta 2 sur un poste, et je me demandais si je devrais le reformater avant d’installer VS2005 RC, comme il fallait le faire avec les précédentes version beta de Visual Studio.

Et bien non, Microsoft fournit une procédure de désinstallation des version beta antérieure, et même un utilitaire réalisant l’opération automatiquement.

Il a part contre fallu réinstaller les templates d’Atlas, le framework AJAX de Microsoft (très prometteur d’ailleurs, j’en reparlerai).

Bref Microsoft s’améliore un peu sur l’installation, et surtout le support des versions beta.


Pour rencontrer Billou…

8 septembre 2005 par Alexis KARTMANN

Allez au symposium DotNetGuru le 24 octobre. Attention c’est quand même au départ une rencontre entre développeurs, alors curieux s’abstenir !


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.