Conclusion: Mathias se lance sur .Net Server

Au cours des 12 derniers épisodes, je vous ai un peu raconté ma vie…
Mais je vous ai surtout fait partagé ce qui m’a sans doute le plus influencé dans le monde du développement .Net/Java.

Maintenant la boucle est bouclée: je me suis lancé sur le développement d’un serveur d’application .Net avec pour influence:

  • EasyPHP
  • Tomcat
  • Spring DM Server
  • OSGI
  • Spring.net
  • COM
  • Gigaspaces
  • Google Chrome
  • Eclipse RCP

Si je reviens au premier épisode d’introduction : Mathias est heureux… mais pourquoi ? je vous montre une pauvre screenshot d’une petite application Winform… mais qu’est-ce que ça cache?

Et bien, cette application exploite mon "moteur à la OSGI" pour :

  • charger des plugins, soit dans un AppDomain, soit dans un processus
  • démarrer dynamiquement les instances de service IService qui font office de point d’entré du plugin
  • demander aux services de se dessiner dans la fenêtre principale

Cela donne quelque chose similaire à Google Chrome, puisque si vous "killer" un process correspondant à un plugin, cela n’affecte pas l’application.

Les plugins peuvent dépendre les uns des autres, ce qui veut dire que le moteur va démarrer les dépendances avant.
Les plugins peuvent communiquer entre eux, comme le fait l’application principale avec le point d’entré IService. Cette communication est faite en .Net remoting (pipe).

Mais ce moteur peut aussi  être utilisé en mode console pour agir comme un serveur d’application!
Vous pouvez alors:

  • lister les "Bundles" qui tourne
  • stopper/démarrer un Bundle, ce qui  va bien sûr démarrer les dépendances nécessaires
  • les bundles sont isolés dans des AppDomains ou Processus différents
  • installer un Bundle à partir d’un ZIP
  • installer un Bundle à partir d’un "repository" (comme dans Spring DM Server)
  • dans le cas de l’installation à partir d’un repository, le serveur va aussi télécharger et installer les dépendances, en tenant compte de la version bien sûr

Vous en voulez plus?

  • Le Repository de Bundle n’est autre qu’une application de type Bundle
  • un Bundle peut être de type "Web", ce qui va démarrer une instance de XSP (serveur ASP.net de Mono)
  • le Bundle "BundleRepository.Web" fournit une interface Web à la gestion du Repository
  • le Bundle "WebManager" fournit une interface Web à la gestion des Bundles et du serveur d’application

Pas mal non?
Ce qui manque à tout cela:

  • Une meilleur gestion des dépendances, exprimée à l’aide d’un XML descripteur de service (comme le XML des plugin Eclipse) basé sur Spring.net
  • Une meilleur communication inter-processus que .Net Remoting, avec gestion d’un annuaire et d’un descripteur de service (comme un WSDL)
  • Des Bundles de type "Job schedulé", avec gestion de Workflow, tout cela avec une interface Web pour leur gestion
  • Le déploiement des Bundles sur plusieurs instances de serveurs (cluster) avec gestion de faillover
  • Un Bundle fournissant une couche de sécurité transverse
  • Un Bundle fournissant un accès à une source de donnée hébergé (SQLite)
  • Un Bundle fournissant un service de type cache (Memcache?)
  • Un Bundle fournissant un service de type Bus (Laharsub?)
  • Un Bundle fournissant un service de "Perf. Counter"
  • Un Bundle fournissant un service de logging transverse
  • Une console d’administration à distance
  • Garantir la compatibilité avec Mono+Linux
  • Des gens motivés pour participer à ce projet OpenSource!
  • Héberger un "Plateform as a Service" sur le Could!!
  • Conquérir le Monde!!!

Alors, qu’en pensez-vous?

Le code source est sur BitBucket (il est aussi sur Github mais beaucoup plus vieux): https://bitbucket.org/grozeille/nsynapse

(PS: je suis super mauvais en nom, j’aurai du l’appeler "carotte" ça passerait mieux)

Episode 12 – Mathias découvre Topshelf

Je vous l’avez bien dit que j’ajouterai des épisodes pour faire durer le suspens :)

Il était primordial que je parle de TopShelf, qui a forcement marqué mon existence.
Pour résumé Topshelf, je dirais que c’est… un serveur d’application!
Je vais peut-être trop loin en disant ça, mais c’est la voie qu’il est en train de prendre…

Une des premières fonctionnalités de Topshelf, est de pouvoir développer une application qui peut s’exécuter en mode "console" ou en mode "service Windows": vous n’avez plus d’excuse pour avoir besoin des droits d’admin ! ;)

La deuxième, tout aussi importante, est de pouvoir déployer votre application sans avoir besoin de redémarrer votre service Windows: Topshelf agit comme une application Web ASP.Net, ne lock pas vos DLLs, exécute votre application dans un AppDomain isolé, et relance l’application après détection d’un changement de celles-ci.
C’est donc bien un "conteneur d’application" !
Vous pouvez y déployer autant d’application que vous voulez dans un service Windows sous la forme d’une DLL, le bootstrap de Topshelf s’occupe de son cycle de vie.

Suite naturelle de son évolution, Topshelf est fourni maintenant avec une "application" déployé par défaut: Topshelf Dashboard. Cette dernière offre un portail Web pour pouvoir stopper/démarrer ces applications.
Ça ressemble donc fortement à mon projet :)
Il est clair que c’est une très bonne source d’inspiration pour améliorer encore plus mon projet.

Episode 9: Mathias découvre MEF

Après cette petite pause sur COM la dernière fois, revenons sur le sujet des clients lourds et de la "composition".

Je vous ai déjà BEAUCOUP parlé de l’IDE Java Eclipse et la richesse de son framework de plugins basé sur OSGI.

Je vous ai parlé de .Net qui rattrape son retard à l’aide d’IOC et de MVVM.

Mais je ne vous ai pas encore parlé de VisualStudio.
Ce dernier offre depuis la version 2008 un noyau indépendant de l’IDE pouvant être utilisé pour n’importe quel type d’application : VisualStudio Shell.

Cella est possible à l’aide d’une gestion de « plugins » comme Eclipse (appelé « add-in »).
La gestion des add-ins de VisualStudio s’appuie sur COM (voila pourquoi il fallait que j’en parle avant ;) ).
Cela n’est pas étonnant puisque c’est LA technologie de communication inter-processus sur Windows.

Mais récemment, Microsoft a travaillé sur une nouvelle technologie d’activation de service et de "gestion de dépendances" et cette fois-ci 100% .Net. Cette dernière a été utilisée dans VisualStudio, afin de ne charger en mémoire que les add-in nécessaire suivant le besoin.

Cette technologie s’appelle MEF : Managed Extensibility Framework.
Et MEF fait du bruit : il est inclus officiellement dans .Net 4.0, Mono l’inclut dans la version 2.8 (car MEF est OpenSource, BRAVO Microsoft), et MEF est annoncé comme « remplaçant de l’IOC ».
En fait, même si on déclare des dépendances, ce n’est pas une solution IOC/DI…
D’ailleurs, je n’ai eu que de mauvais écho sur son sujet: mauvais usage, trop jeune?

Ce qui est bon à prendre, c’est l’aspect "attributs" pour déclarer ce qui serait visible à l’extérieur ou dans le conteneur IOC. Cela fait aussi pensé à la JSR-299 qui propose des annotations standards en Java pour tout type de conteneur IOC.

J’ai aussi exploré d’autres pistes pour une gestion de "plugin": Mono.Addin. Je trouve le principe très bien, et très inspirant, mais peut-être trop spécifique à ce pourquoi il a été inventé, cad, la gestion d’add-in dans Monodevelop.
Cette solution s’appuie aussi sur les attributs pour déclarer les Addins, donc c’est une bonne voie à suivre.

Episode 8: Mathias découvre COM

Bonjour tout le monde, et merci de votre patience…
Ça fait presque 5 mois que je n’ai pas blogué, et que j’ai interrompu ma série "Mathias découvre…"
Comme j’en découvre tous les jours, j’ajouterai bien d’autres épisodes avant la conclusion finale, mais j’ai peur que ça dure une éternité :)
En réalité, je voulais "jouer la montre" avec ces blogs, afin de stabiliser mon projet et lui trouver un nom sympathique. Mais je n’ai pas vraiment trouvé le temps d’y travailler, alors je vais "pousser l’oisillon hors du nid", et j’ajusterai plus tard s’il ne vole pas très bien ;D

Je change de sujet par rapport aux précédents postes, et je m’attaque à un autre sujet épineux: la communication inter-processus.

Au début de ma vie professionnel, je n’ai pas commencé à travailler en .Net, mais en Delphi.

J’avoue que Delphi était séduisant. Avant .Net, c’était la manière la plus efficace de réaliser des applications Windows lourde avec un Designer d’interface unique. Et puis, n’oublions pas que l’inventeur du langage Delphi n’est autre que Anders Hejlsberg, qui fut embauché par Microsoft pour inventer C# !

Quand .Net prenait de plus en plus d’ampleur,  j’ai alors réussi à convaincre tout le monde de s’y mettre.
Mais comme la migration devait se faire petit à petit, il fallait intégrer Delphi avec .Net.

Pour cela, j’ai découvert COM.
COM est une technologie 100% Microsoft, mais qui se base sur les mêmes principes que Corba.
COM permet de communiquer entre les applications, peu importe le langage. La communication se faisait en activant des services, à l’aide d’un contrat qui se rédigeait dans un langage indépendant du langage de compilation : IDL (Interface Definition Language).

C’était magique : on pouvait inclure un UserControl .Net dans une application Delphi existante ! On pouvait aussi appeler des services .Net depuis Delphi, et vis vers ça !

Les services étaient d’ailleurs enregistrés auprès de Windows, dans la base de registre. Donc, quand un programme Delphi demande un service qui répond à une interface, Windows se charge de le localiser (DLL ou EXE) de l’héberger dans un conteneur (dans le cas de l’EXE, il lance ce dernier, dans le cas de la DLL, il l’host dans DLLHOST.exe) et d’instancier le service pour qu’il puisse être utilisé.

Et ce n’est pas tout ! Il y avait aussi DCOM qui est la version distribué de COM. Cela veut dire que si la DLL n’est pas sur la machine actuelle, Windows se charge d’interroger les autres serveurs DCOM pour qu’ils instancient le service à distance ! Tout cela avec une couche de sécurité ultra complexe !

De plus, le langage IDL supporte les méthodes, les propriétés, les événements, les paramètres de type « out », l’héritage d’interfaces, la gestion des versions, etc.…

Si vous souhaiter exposer des services sous Windows, cette technologie semble la plus appropriée.

Dans le monde des serveurs J2EE, ce fût Corba, concurrent direct à COM, qui remplissait ce rôle.

Mais voila : COM est 100% Windows, 100% Natif (non managé) ce qui rend le pont COM-.Net peu performant.

Concernant CORBA, il existe bien des connecteurs pour .Net, mais pas de serveur CORBA 100% managé.
De plus, CORBA est abandonné dans certains domaines à cause de sa lourdeur : les distributions Linux ont migré de CORBA à DBUS pour avoir aussi leur « équivalent à COM ». A noter que DBus existent en 100% .Net: http://www.ndesk.org/DBusSharp

Microsoft abandonne d’ailleurs COM pour sa technologie de communication phare : WCF.
Avant WCF, Microsoft avait introduit une autre solution 100% .Net: .Net Remoting. Mais ce dernier était trop simple et a fini par être abandonné, même si c’est toujours la technologie par défaut pour communiquer inter-AppDomain.

Alors, que choisir comme technologie de communication et d’activation de service ? Point à Point ou par un intermédiaire (Bus/Broker)? Quel format de message/marshalling doit-on utiliser?

Pour ma part: COM n’est pas multi-plateforme, je ne suis pas convaincu par l’usine à gaz WCF, DBusSharp est trop bugué, .Net remoting est trop simple/limité… je reste un sur ma fin.

Mais entre temps, j’ai découvert le "Messaging" et la communication asynchrone, qui est parfaite pour un environnement distribué. On ne peut pas remplacer le RPC par le Messaging dans tous les cas, mais il a falloir que j’exploite cette voie.

Episode 7 : Mathias découvre Unity+WPF+MVVM aux Techdays 2010

Je vous ai parlé de ma découverte de .Net dans l’épisode 6.
J’ai constaté un gros contraste avec Java: j’avais l’impression que Java était compliqué (EJB et tout ça) et que .Net était simple.

Cette simplicité n’est pas toujours positive, on peut même dire que Microsoft est "maudit" par ce fléau: rester attractif pour les développeurs débutants.

En général, cela donne des frameworks "sales" avec un fort couplage du code métier avec les interfaces, impossible à tester unitairement bien sûr.

Pour pouvoir découpler tout cela, on distingue 3 couches:

  • L’interface, appelée "view" ou "presenter", c’est le moyen d’afficher l’information
  • La logique métier, appelée "model", c’est ce que l’on va pouvoir tester à l’aide de tests unitaires
  • Les actions ou commandes, qui représente une action de l’interface par l’utilisateur qui va déclencher un traitement

Mais quand on sépare les responsabilités en plusieurs objets, on se retrouve avec un nouveau problème : comment coupler les vues, contrôleurs, etc. ? Comment "broker" les actions ?

Il y a une solution à cela: l’IOC. On peut dire aussi que le Databinding en est une autre.
Si Microsoft avait un train de retard sur les Framework IOC, ils connaissaient néanmoins déjà le problème depuis un moment, puisqu’ils l’avaient en partie résolu dans CAB.
C’est donc sans surprise que Microsoft nous livre son framework d’IOC "officiel" basé sur l’existant de CAB : Unity.

A la sortie d’Unity, j’étais déjà tellement convaincu par Spring.net que je n’ai jamais adopté le framework de Microsoft. De plus, les alternatives OpenSources étaient  bien présentes: Castle Windsor, Ninject, Autofac, etc.

A noter que les framework tel que CAB/RCP rendent vos clients lourds "extensibles", ce qui veut dire qu’un plugin va pouvoir greffer de "vues" dans l’interface, et s’abonner aux "commandes": chose tout à fait faisable avec de l’IOC.

Mais les frameworks IOC ne font pas tout, et ne suffisent pas à fournir une véritable architecture MVC pour client lourd, même s’ils peuvent nous y aider (voir mon poste précédent).

Pour résoudre le fléau du couplage fort entre l’interface et le code métier, Microsoft a sortie quelque chose de révolutionnaire: WPF et Silverlight!
Avec la venue de WPF et Silverlight, la communauté .Net OpenSource a commencé à se pencher alors sur le problème du MVC pour les applications lourdes. Divers frameworks apparaissent alors, comme Prism: http://compositewpf.codeplex.com/

Mais le MVC, comme on le connait dans le monde Web, ne s’applique pas bien aux clients lourds, surtout si on veut exploiter le Databinding et les événements.

Très franchement, je ne me suis jamais vraiment passionné pour WPF/Prism/etc. mais je voyais bien que quelque chose de gros était en train de venir…
J’ai ensuite assisté aux Techdays 2010 et j’en ai profité pour me mettre à jours sur le sujet.
Les sessions étaient d’ailleurs très accès sur Silverlight/WPF mais aussi sur le nouveau modèle MVVM (Model-View-ViewModel), et avec une bonne dose d’Unity bien sûr…
J’ai alors été très agréablement surpris par ces présentations, surtout celle qui montre comment supprimer tout le « code behind » des interfaces XAML avec des ViewModel et des Commands.

Pour mieux comprendre de quoi je parle, aller voir vite le screencast ici!!!

Bref, je ne sais pas si vous me suivez, mais l’IOC, le RCP, le MVC, la gestion de plugins, tout ça c’est intimement lié !

Petit à petit, .Net rattrape le retard vis-à-vis d’Eclipse RCP !
On peut même dire que .Net a pris de l’avance avec Silverlight/WPF, puisque la rédaction des interfaces en XML n’est qu’au stade de proposition pour Eclipse 4: http://wiki.eclipse.org/images/a/ab/XWT.pdf

Episode 6: Mathias découvre .Net!

En fait, je triche, les épisodes ne sont pas dans l’ordre chronologique.
Je n’ai pas découvert .Net après Java, mais en même temps.

C’était le 6 Janvier 2005, j avais blogué à ce sujet: j’avais découvert qu’avec Mono, il était possible d’exécuter un même programme EXE sous Windows comme sous Linux ! J’en avais la larme à l’œil…

Ce qui m’a le plus impressionné c’était de voir qu’il était possible d’exécuter du Java dans .Net grâce à IKVM ! Cela marchait tellement bien qu’ils ont réussi à exécuter Eclipse sous .Net ! Mais ce n’était qu’expérimental à l’époque…

En découvrant .Net, j’ai tout de suite pris conscience du retard par rapport à Java, et j’ai alors voulu m’y atteler.
J’ai alors commencé à réaliser un framework « RCP » comme celui d’Eclipse, mais full .Net. Voici le résultat à l’époque :

On pouvait réaliser des plugins qui fournissent des vues qui s’encrent dans différents endroits, que ce soit dans la fenêtre principale ou dans un onglet d’édition.
J’en était plutôt fier, encore aujourd’hui… j’ai bien envie de le porter avec les technos modernes d’aujourd’hui…

C’est aussi à cette époque que j’ai découvert Spring, et au même titre Spring.Net, que j’ai utilisé dans mon entreprise pour réaliser, avec Romain, un framework MVC Winforms.

J’ai aussi découvert d’autres framework IOC, et par la même occasion CAB de Microsoft (Composite Application Bloc), mais sans en être convaincu.

A l’époque, on commençait à parler d’OSGI, mais je n’avais pas assez de connaissance dans le domaine

En conclusion, je restais sur ma fin avec .Net, et j’en étais même frustré. Mais j’ai toujours été convaincu que c’était aussi "une terre d’opportunités" et que je pouvais faire quelque chose…
L’IOC, le MVC pour client riche, RCP, OSGI… les idées émergeaient déjà, mais n’était pas matures.

Sonar pour .Net!

Pour fêter la sortie de la version 0.4 du plugin Maven pour .Net avec l’intégration à Sonar 2.3, je voulais féliciter Alexandre Victoor et Jose CHILLAN: BRAVO!

Et comme des images sont souvent plus parlantes que de longs discours, voici un extrait de ce qui est possible de faire:

100% de couverture de test avec ASP.Net

Dans ma quête de la couverture de test absolue, j’ai décider de tester mon application ASP.Net avec des tests fonctionnels.
Pour cela, j’utilise le combo: Specflow+Selenium.

En terme de rédaction du test, cela donne ça:

Feature: Test with ASP.net
	In order to get test coverage of my ASP.Net Application
	As a developper
	I want to run an embeded ASP.Net server in my NUnit test

@selenium
Scenario: Say Hello to Michel
	Given I'm on the default page
	When I enter the name "Michel"
	And I click the "SayHello" button
	Then the message is "Hello Michel"

Grâce à Specflow, je peux donc rédiger mon scénario en anglais, très lisible par l’utilisateur qui va pouvoir ainsi exprimer son besoin.
Ces phrases sont ensuite couplées à des méthodes C# qui effectuent une partie du test. Exemple:

[When("I click the \"SayHello\" button")]
public void IClickTheSayHelloButton()
{
    selenium.Click("SayHelloButton");
    selenium.WaitForPageToLoad((1 * 60 * 1000).ToString());
}

On remarque ici que j’utilise Selenium pour simuler le scénario et effectuer un click sur un bouton de la page.

Petite astuce: le scénario est "tagué" @selenium. Grâce à ce tag, je peux indiquer qu’il faut initialiser Selenium à chaque début de scénario tagué ainsi.

[BeforeScenario("selenium")]
public void BeforeScenario()
{
    var firefoxPath = Path.GetFullPath(
        Path.Combine(AppDomain.CurrentDomain.BaseDirectory, @"..\..\..\Libs\Firefox\firefox.exe"));
    var selenium = new DefaultSelenium("localhost",
                                       4444,
                                       @"*firefox " + firefoxPath,
                                       "http://localhost:8123");
    ScenarioContext.Current["selenium"] = selenium;
    selenium.Start();
}

OK, je suis content avec ça, je peux maintenant rédiger des tests fonctionnels lisibles et les automatiser à l’aide de Selenium.
Mais cela ne me donne pas la couverture du code de mon application .Net qui se trouve hébergée sur un serveur IIS :(

Afin d’avoir la couverture du code de toute l’application, j’ai alors décider d’exécuter l’application ASP.Net à l’intérieur du processus du test unitaire!
Afin d’héberger l’application ASP.net, j’utilise pour cela la librairie de Mono: XSP.
Pour cela, quelques lignes de codes suffisent:

[BeforeTestRun]
public static void BeforeTestRun()
{
    const int port = 8123;
    string path = Path.GetFullPath(@"..\..\..\MyApp.Web");
    const string webServerFileName = "Mono.WebServer2.dll";

    string sourcePath = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, webServerFileName);
    string destinationPath = Path.Combine(Path.Combine(path, "bin"), webServerFileName);

    File.Copy(sourcePath, destinationPath, true);

    var websource = new XSPWebSource(IPAddress.Any, port);
    webAppServer = new ApplicationServer(websource);
    webAppServer.AddApplication("localhost", port, "/", path);
    webAppServer.Start(true);
}

[AfterTestRun]
public static void AfterTestRun()
{
    webAppServer.Stop();
}

Simple non?

Voila alors le résultat:

Afin de pouvoir tester par vous même, vous pouvez télécharger les sources depuis http://bitbucket.org/grozeille/testwithaspdotnet/src/, lancer le serveur Selenium à l’aide de "SeleniumServer.bat", lancer les tests avec "Test.bat", et voir le rapport de couverture de code avec Libs\PartCover .NET 2\PartCover.Browser.exe en ouvrant le fichier Coverage.Xml.
Ça, ça rox du poney!

Spring.Net, ASP.Net, Session et multithreadind

Pour commencer, je vous préviens, ce billet s’adresse à ceux qui savent développer en ASP.Net.
Je ne plaisante pas! J’ai vu un trop grand nombre de gens prétendre savoir faire des applications Web sans savoir m’expliquer le protocole HTTP!
Donc voici un petit test d’entré:

  • Comment fonctionne un HttpHandler?
  • Pourquoi ne pas utiliser les UpdatePanels?
  • A quoi sert un MembershipProvider et comment s’en servir?

Si ces questions vous paressent obscures et que vous êtes plutôt un développeur “glisser-déposer”, alors je vous propose de vous “glisser-déposer” sur un autre blog ;)

Pour les autres, j’ai un aveu à vous faire: je viens de découvrir qu’il n’est pas possible d’afficher 2 pages ASP.Net en simultané si elles accèdent à la Session.

Lire la suite

As a Geek I want to blog

S’il y a bien quelque chose qui me fait stresser, c’est de livrer un logiciel sans être sûr qu’il fonctionne.

Livrer une version sans la tester, c’est un peu comme jouer à la roulette russe.
C’est pourquoi depuis longtemps je pratique les tests unitaires avec NUnit.

Mes tests étant de plus en plus complexe, j’ai appris aussi à utiliser RhinoMocks afin de simuler les dépendances de l’objet du test.

J’ai toujours fournis des efforts dans les tests: alors POURQUOI je stress toujours autant avant de livrer une version?

Simple: car malgré les tests et une bonne couverture, je livre toujours des bugs.
Le fait est que, les tests unitaires valident chaque briques de manière indépendante, mais pas toute l’application dans son ensemble.
De plus, faisant beaucoup d’application Web, je me dois de tester l’interface HTML + Javascript, ce que je ne fais pas pour l’instant.

J’ai bien eu des tentatives avec Selenium, mais sans trop de résultat.
J’ai bien envisagé JSUnit, mais je n’y suis pas encore là.

Mais même si je test tout de manière indépendant, le plus gros des problèmes survient lors de l’intégration de toutes ces briques, avec par exemple des bugs dans les fichiers de configuration Spring.net.

Si Selenium me permet de tester cette intégration, j’ai tout de même un gros problème: je ne peux pas prédire les données de mon test, ce qui fait que parfois il passe, et parfois il ne passe pas.

J’ai aussi rencontré un autre problème avec les tests unitaire: la rédaction de ces derniers.
Quand je souhaite tester 2 scénarios différents, mais similaire à 90%, je me retrouve avec beaucoup de copier/coller.

Je factorise alors, mais mon test devient rapidement difficile à lire, alors que pourtant il peut servir de documentation du fonctionnement du code.

Après quelques recherches, et la lecture de l’excellent blog de Steve Sanderson, je me suis mis au BDD!

Le principe est de décrire un besoin utilisateur dans sa langue native: non, pas le C#… l’anglais!
Cela donne quelque chose comme:

Feature: Test SpecFlow with Selenium and RhinoMocks
In order to test Specflow
As a geek
I want to write stories with Specflow
And test the application with Selenium
And mock the backend with RhinoMocks

Le truc, c’est que cela va me servir de Spec, ce qui va donc orienter la conception de mon application.

C’est déjà l’objectif du TDD: concevoir une architecture à partir des tests, cela nous permet de réaliser QUE CE QUE L’ON A BESOIN: c’est le principe du KISS ou YAGNI.

Mais ces tests peuvent être difficile à concevoir, mais le BDD nous aide à décrire nos tests!
Si quelqu’un débarque sur le projet, et lit un test de 100 lignes de code, il va peut-être pas comprendre du premier coup. Mais s’il lit la version “Anglaise”, à moins qu’il ne sache pas lire, il va alors comprendre le test.

Cette magie se fait à l’aide de SpecFlow, qui est la version 100% .Net du très à la mode Cucumber (ou Cuke4Nuke).

Au programme: rédaction des tests “en Anglais”, intégration dans VisualStudio, et génération d’un test unitaire découpé en étapes (une étape par phrase).
Cela rend alors les tests unitaires vraiment TRES TRES lisibles.

Reprenons mon cas, ou je souhaite tester une application Web dans son ensemble.
Afin de tester l’interface Web, je vais implémenter les étapes de mon BDD à l’aide de Selenium.

OK, j’ai maintenant un test compréhensible par un utilisateur… J’ai un test NUnit C# correctement découpé et donc plus lisible par les développeurs. Je pars du test pour définir mon UI, qui elle même va orienter le design de mon Controller et ainsi de suite… Je simule les clicks de souris afin de naviguer dans le site, et je détecte les erreurs “d’intégration”…
Mais j’ai toujours un même problème: je ne maitrise pas les données de ma base afin de vérifier le fonctionnement de mon test.

Dans ce cas, il me faut simuler ma couche d’accès aux données à l’aide de mocks RhinoMocks.
Mais si mon test unitaire s’exécute sur un poste client, et que le site Web est hébergé sur un serveur dans un processus différent, comment puis-je faire mes mocks?

Grâce à Steve Sanderson, nous avons la solution, et elle s’appelle DELEPORTER.

Le principe est plutôt simple: un module HTTP agit comme un serveur .Net Remoting.
Le client sérialise un Delegate (anonyme ou pas, ou lamdba) afin de l’envoyer sur le serveur et de l’exécuter dans son processus.

C’est clairement une grosse faille de sécurité, alors pensez à bien ségréguer les configurations de tests et celles de production!

Etant donné que j’utilise Spring.net pour l’injection de dépendance, je peux injecter de Mocks à l’aide d’une petite factory RhinoMock.
Mon test va ensuite définir le comportement de ces derniers pour mon cas de test.

Au final, cela donne:

// J'utilise Deleporter pour exécuter ce code coté Serveur
Deleporter.Run(() =>
{
        // je demande à Spring.net d'obtenir le MockRepository de RhinoMock
	var mocks = ContextRegistry.GetContext().GetObject("MockRepository") as MockRepository;

        // je demande à Spring.net d'obtenir le mock déjà créé et injecté
	var repository = ContextRegistry.GetContext().GetObject("MyRepository") as IMyRepository;

        // j'informe RhinoMock que je souhaite recoder le comportement de mon mock
	mocks.BackToRecord(repository);

        // Quand "GetMessage" sera appelé, je souhaite retourner "Welcome Mathias"
	Expect.Call(repository.GetMessage())
                .Repeat.Any()
                .Return(string.Format("Welcome {0}!", userName));

        // j'informe à RhinoMock que j'ai terminé de spécifier le comportement du mock
	mocks.Replay(repository);
});

Il ne reste plus qu’à exécuter le test Selenium, vérifier les données de l’UI, et la boucle est bouclée!

Les sources de cet exemple sont disponibles sur mon BitBucket: https://bitbucket.org/grozeille/testspecflow.mvc

Suivre

Recevez les nouvelles publications par mail.