17778 items (17189 unread) in 65 feeds
BlogspersosFR
(1169 unread)
BlogsEntreprisesFR
(1384 unread)
JavaJEE
(4873 unread)
Diversdev
(5153 unread)
Videos
(200 unread)
Agiles
(1783 unread)
divers
(822 unread)
Wicket
(54 unread)
Spring
(31 unread)
Architecture
(287 unread)
BlogspersosAutre
(468 unread)
BlogsEntreprisesAutre
(965 unread)
I’ve been working with FitNesse.slim, using the cslim implementation to execute C++ code. I have some rough notes online. These should be enough to get started, though you’ll need to be using G++ 4.4 or later.
In any case, have a look. Give me some feedback if you’d like. I’ll be working on these over the next month or so: [schuchert.wikispaces.com]
The GlassFish admin console is often cited as one of the strong point for GlassFish. Yes, open source and ease-of-us can live happily together! After delivering clustering and centralized admin features in Milestones 1 (post) and 2 (post) of the ongoing 3.1 work, it was time in Milestone 3 to deliver the first drop of a graphical user interface that is able to interact with these features.
The following is a short screencast (hosted on the relocated "GlassFish Channel" property) walking you through the basic scenario of creating a cluster, populating it with instances, starting the cluster, deploying an application to various targets (cluster or standalone instances) and closing with a short part on monitoring the system - all from the Graphical User Interface :
The GlassFish Open Source Edition 3.1 Promoted Builds are available from [download.java.net] and numerous details are offered on the GlassFish Wiki for this milestone and the upcoming work.
Check out [youtube.com] for more videos soon.
My trip to Jess's wedding (on the Lost Coast of Northern California) started with a flight to Portland, Oregon. After arriving, I drove to Clint and Autumn's house in Eugene where we enjoyed some sweet Oregon micros and reminisced about Costa Rica. The next morning, we headed for the wedding; an 8-hour drive. Our road trip was awesome, especially when we started driving through the Redwood Groves on 101.
We stayed in a sweet beach house for the weekend. While it was foggy most of the time, the sun did come out on Saturday. We quickly became surrounded by beautiful views and headed to the beach to relax with Jess.
The wedding was on Sunday, a mere block from where we were staying. The ceremony was one of the most heartfelt I've ever heard, especially since the Wedding Official was a friend of the bride's since she was born.
The reception afterwards was a truly spectacular party that lasted well into the evening. Clint and I vowed to go to bed early, but we ended up having so much fun we closed the place down. Jess and Lili were an instrumental part in creating a spectacular night, especially with their wedding dance and infectious happiness.
The next day, we woke up on time, embarked on the 10-hour road trip back to Oregon and enjoyed a quick detour through the Avenue of the Giants. I did end up missing my flight home, but it was worth it. Thanks to Lili and Jess (and their families) for showing us such a great time. It was truly spectacular.
For more pictures, see albums on Flickr, Facebook or the slideshow below.
Si mon livre a de bonnes critiques de la part de ses lecteurs, c'est en grande partie à mes relecteurs que je le dois.
C'était l'année dernière, en été, j'écrivais laborieusement et j'envoyais mes chapitres au fur et à mesure de leur rédaction, dès que j'avais quelque chose de lisible. Je les envoyais à mes 3 relecteurs principaux[1], à qui je donnais quelques jours pour me faire des remarques. Et ils l'on fait ! Rétrospectivement, cela m'a été d'une grande... Lire Mes relecteurs

Les Agilistes de ce monde sont au fait de l’importance des tests automatisés pour assurer la pérennité d’un projet informatique. En fait, tout développeur qui a tenté la pratique du TDD et qui a réussi à inclure cette pratique dans son travail au quotidien découvre rapidement le sentiment de puissance qui en résulte. Ce sentiment de puissance prend sa source dans le fait que le développeur a beaucoup plus d’assurance et de confiance dans le logiciel qu’il développe. Le fait d’avoir plus d’assurance et de confiance laisse une meilleure impression au client et inévitablement résulte en une équipe plus heureuse. Cependant, afin de toujours s’assurer de livrer le maximum de valeur au client, il est parfois nécessaire d’ajuster le niveau de couverture des tests selon le contexte du projet.
Dans le cadre du développement d’une ‘preuve de concept’ (prototype), par exemple, il y a de fortes chances que le développement du prototype ne se poursuive pas pendant des mois. Le système ayant une taille réduite, l’équipe peut limiter la couverture des tests unitaires à certaines parties critiques du code car il est moins probable que le système régresse en qualité sans que l’équipe découvre rapidement une anomalie. Ainsi, s’appuyer simplement sur quelques tests manuels peut être un choix judicieux lorsque l’équipe a peu de temps pour développer les fonctionnalités requises afin de répondre aux objectifs d’affaires du prototype, à condition bien sûr de ne pas reprendre le développement du système à partir du prototype (ce qui se produit malheureusement dans la plupart des cas). Dans un projet Web MVC, par exemple, une équipe pourrait décider de tester uniquement le cœur du domaine d’affaires sans tester les contrôleurs ni les vues.
Le fait de couper sur les tests sous prétexte qu’on veut livrer plus de fonctionnalités d’un système dont le développement se déroule sur plusieurs mois à l’aide d’une approche Agile de développement logiciel est un choix qu’on pourrait qualifier de risqué. Un tel choix peut ralentir sérieusement l’avancement du projet car le risque d’endommager les bouts de logiciel déjà livrés sans test augmentera de façon quasi exponentielle sprint après sprint. En plus de provoquer l’inverse de l’effet escompté, ce genre de choix augmentera significativement le coût de développement du système. De plus, cette diminution de qualité créera nécessairement des tensions entre le client et l’équipe, ce qui nuira significativement au plaisir de développer du logiciel.
On constate ainsi que la définition de « Just Good Enough » pour les tests varie significativement d’un contexte de développement à l’autre, d’où l’importance de l’établissement d’une définition de ‘Terminé’ bien adaptée à la situation.
Servlet 3.0 has some very nice ease-of-development features in the form of new annotations (such as @WebServlet) and some useful extensibility features with web-fragments (details here) and ServletContainerInitializer which all contribute to make web.xml optional. While this is a great step towards more modular applications and an overall development simplification, you may want to have more control over what's being deployed in your production system. This is what metadata-complete helps you achieve.
web.xml contains a metadata-complete attribute in the web-app element whose binary value defines whether other sources of metadata should be considered when deploying this web application, this includes annotations on class files (@WebServlet, but also @WebListener, @WebFilter, ...), web-fragment.xml as well as classes located in WEB-INF/classes. If set to true all of these will be ignored and web.xml is the only metadata considered. Setting it to false falls back to the default behavior. Note you can also use this attribute inside fragments to have them consider annotation or not. Check the Servlet 3.0 specification (section 15.5) for details on that part.
Better than a long blog entry, here's a short screencast (available in HD) illustrating the basics of this feature :
You can try this all today in GlassFish.
Here are other Java EE 6-related "lesser-known features" :
• Module initialization order in Java EE 6
• WEB-INF/lib/{*.jar}/META-INF/resources
• Just say no to Scarlett Johansson ... (but say yes to Bean Validation)!
• javax.annotation.ManagedBean
• EJB 3.1 asynchrony and transactions

Afin de tester nos développements iPhone nous utilisons largement les frameworks Google-Toolbox-for-Mac et OCMock (cf Tests unitaires et tests d’interface sur iPhone : État des lieux)
Après la mise à jour du SDK4 et le passage à Xcode 3.2.3 nous avons eu une désagréable surprise : nos frameworks de tests ne compilaient plus et jusqu’à aujourd’hui on trouve très peu d’information sur la façon de régler ce problème.
Voici comment nous nous y sommes pris :
L’erreur de build rencontrée est la suivante :
Undefined symbols:
"_OBJC_CLASS_$_OCMockObject", referenced from:
objc-class-ref-to-OCMockObject in WebServiceTest.o
ld: symbol(s) not found
Jusqu’à maintenant nous ajoutions la bibliothèque OCMock.framework à la phase de build « Link Binary With Libraries ». Il semble que l’on ne peut plus utiliser la version précompilée du Framework disponible dans la section téléchargement du site d’OCMock.
Pour faire fonctionner OCMock avec Xcode 3.2.3 vous devez :
Vous buildez à nouveau et… rien ne se passe.
Apres OCMock c’est maintenant le script de Google-Toolbox-for-Mac qui reste bloqué indéfiniment sur cette erreur :
SBSetAccelerometerClientEventsEnabled failed: (ipc/
send) invalid destination port
Là encore la solution est de récupérer les sources de GTM sur le trunk du repository, puis de remplacer tous les fichiers nécessaires aux tests iPhone comme indiqué dans le guide d’installation :
http://code.google.com/p/google-toolbox-for-mac/wiki/iPhoneUnitTesting
Maintenant vous pouvez enfin relancer vos tests !
Suggestion d'articles :
Pour faire plaisir à votre chef de projet, ancien développeur, vous le laissez occasionnellement développer quelques fonctionnalités mineures de votre application.
Aujourd'hui, vous lui avez demandé d'itérer sur une liste pour en afficher le contenu : "Hello World". Malheureusement, un peu rouillé et distrait par son départ en vacances prévu pour le soir même, il a malencontreusement oublié de mettre "World" dans la liste lors de son initialisation ! Pour réparer cette erreur, il l'a inséré un peu plus loin, mais son code ne fonctionne plus et il ne comprend pas pourquoi ("ça doit être un bug de la JVM").
Avant de partir en vacances, il vous charge donc du problème, avec les instructions suivantes :
FIXME indique la ligne où il vous autorise à insérer du code correctifSaurez-vous vous montrer à la hauteur de la confiance que vous accorde de votre chef de projet bien-aimé ?
- final List<String> list = new ArrayList() {{ add("Hello"); }};
- final Iterator<String> iterator = list.iterator();
- System.out.println(iterator.next());
- list.add("World");
- // FIXME : insert some magic code here
- System.out.println(iterator.next());
Note : ce quiz a été réalisé avec l'aide de Romain Revol, que j'ai eu le plaisir de former au cours Java Spécialist.
Réponse bientôt !... Lire Java Quiz #40
For the past two weeks I’ve used DuckDuckGo as my search engine. It’s a search engine built on open-source software. What made me switch is the capacity to navigate through search results with the keyboard using vi-like keys (hjkl).
I didn’t know at the time but Google also allows you do something similar. You just need to go to http://www.google.ca/experimental/ (also works for google.com and I guess most other domains). There you can opt-in to be part of the Keyboard shortcuts experiment. That’s it! J/K are used to navigate through search results, O (or enter) to open them.
Hopefully this is going to be useful for the other keyboard junkies out there.
La vie d’un développeur ce n’est pas toujours de travailler à la création d’une nouvelle application. Au contraire nous serons souvent amené à faire ce que j’appelle de l’Archéologie. Cela veut dire entrer dans du code écrit il y a quelques années, être capable de faire fonctionner une vieille application, et surtout, être capable de la moderniser sans l’abimer. J’ai regroupé quelques outils et quelques pratiques apprises ces dernières années, mais n’hésitez pas à compléter.
Règle 1 : respectez les pièces rares
Règle 2 : protéger avec une bâche
Règle 3 : prenez des radiographies du patient
Règle 4 : commencez par ce qui n’est pas cher
Règle 5 : utilisez des scanners pour détecter les peintures cachées
Règle 6 : mettez en place un suivi
Règle 7 : pensez au futur
Règle 1 : respectez les pièces rares
Dans chaque logiciel, dans chaque application que j’ai vu, il y a un petit coeur qui bat. Il y a souvent quelques classes particulièrement compliquées, dont l’historique sur Subversion est très chargé. Mon premier conseil : respectez le travail de vos anciens. Imaginez qu’à l’époque où ce code a été construit, ils n’avaient pas les outils, les blogs et l’information dont vous disposez. Soyez donc humble et ne critiquez pas à tort et à travers la qualité du code ou son architecture de l’époque. Avec un peu de chance, un EJB 2.1 Statefull codé en Facade avec des EJB 2.1 Stateless derrière prendra de la valeur avec les années… ou pas.
Plus sérieusement, il y a des parties de code qu’il convient de respecter. L’historique des bugs corrigés raconte une histoire, l’évolution du logiciel et les problèmes rencontrés par l’équipe. Je conseille de passer du temps avec les développeurs encore présent, et de bien faire attention à ne pas effacer ces fichiers, qui racontent une histoire.
Un bug corrigé c’est un bug corrigé…. Sur des problèmes compliqués comme le pricing d’une Option, croyez moi vous ne faîtes pas le fier.
Règle 2 : protégez avec une bâche
Lorsque vous vous lancerez dans du refactoring, il est important d’isoler votre travail du reste du projet. Identifiez clairement les dépendances et les limites de votre travail, et dîtes-vous : je m’arrêterai là. Utilisez par exemple mvn dependency:analyze et mvn dependency:tree pour regarder le couplage de vos modules, si vous utilisez Maven. Il est très important de définir le périmètre des classes ou des packages sur lesquel vous allez intervenir, et de vous y tenir. Sans quoi vous vous retrouvez à réécrire le gestionnaire de clic de la souris alors que vous aviez commencé à modifier une JTable…
Règle 3 : prenez des radiographies du patient
Peut-être la règle la plus importante : blindez le code existant avec des tests unitaires via JUnit, des mocks avec Mockito. Sachez qu’il est possible de mocker des classes concrètes avec EasyMock Class Extension 2.2 depuis fin 2007. Je préfère Mockito qui est plus léger et qui force à coder ses tests à la sauce Given/When/Then.
Les tests pour moi sont la radiographie du patient. C’est la garantie que votre amélioration ne casse pas le logiciel. Faites des tests d’intégration avec Fit par exemple, ou FitNess. Bref c’est de la non-régression, le B.A Ba du bon développeur.
Enfin si vous faîtes du tuning, prenez des mesures avant de commencer à modifier une ligne de code. Combien de temps pour traiter en moyenne un fichier de 50 000 lignes ? Combien de temps pour charger la 3ème page de la liste des produits ? Pensez à prendre scrupuleusement des notes et des chiffres avant de toucher quoique ce soit. Cela s’applique aussi au temps nécessaire pour compiler votre projet par exemple. Bref pensez chiffres.
Règle 4 : commencez par ce qui n’est pas cher
Ce qui est cher est risqué. Ce qui a plus de valeur par contre, c’est quelque chose de très compliqué que l’on peut corriger très rapidement. Première idée que j’ai en tête : passez à Java 6. Cela ne coûte rien, ou très peu. N’attendez pas que quelqu’un vous « autorise » à le faire. Le pilote de l’avion c’est vous. Le chirurgien c’est vous. Faîtes-le, allez voir le client ou les gens du métier, montrez-leur que le logiciel tourne mieux, et laissez-le ensuite s’occuper du psycho-rigide qui ne veut pas passer à Java 6 alors que vous êtes en Java 1.4. Vous pouvez lui faire peur en expliquant que Java J2SE 5 n’est plus supporté par SUN Microsytems depuis le 3 novembre 2009 afin qu’il se bouge un peu.
Ce qui ne coûte pas cher mais qui est un peu plus risqué, c’est les mises à jour des librairies. Vérifiez que vous êtes à jour quant aux différentes versions de vos Jars. Ce matin, nous avons trouvé un bug dans Hibernate 3.2.6 avec IBM WebSphere, et le lookup du Transaction Manager. En passant à une version plus récente d’Hibernate, le problème est résolu.
Ce qui demande un peu d’effort, c’est la partie Build. Si vous êtes avec Apache Ant et que vous en êtes content, restez avec celui-ci. Ne migrez pas à Maven ou Gradle pour être dans l’air du temps ou pour faire plaisir à un consultant. Si par contre votre build est un tas fumant et qu’il faut 23 minutes pour compiler le projet avec Maven 1, que personne ne sait vraiment comment cela marche… jetez tout. C’est ce que nous avons fait fin 2008 sur un projet avec 15 développeurs. Le build total est passé à 9mn, imaginez le temps gagné par développeur chaque jour.
Maven 2.2.x tourne vraiment bien, et il est rapide. Testez-le aussi, mais attention aux coûts cachés plus tard : il faut s’adapter à Maven afin d’en tirer le meilleur parti, plutôt que de le tordre et d’essayer de le faire marcher selon les noms de ses répertoires, et de son organisation.
Règle 5 : utilisez des scanners pour détecter les peintures cachées
J’ai l’image de ces 2 scientifiques, qui radiographient la Joconde et qui trouvent des traits de dessin de Leonard de Vinci. Imaginez leur surprise et leur joie…
Bon pour nous c’est un peu moins fun, mais attendez de voir où je veux vous emmener. Je vous conseille de tester des outils de profiling et de monitoring, afin d’identifier les points noirs de votre application. Que ce soit en terme de mémoire, comme en terme de CPU, mon préféré est yourKit que j’ai découvert chez Reuters avec Jean-Paul Schemali (spéciale dédicace) et aussi JProfiler, que j’ai beaucoup utilisé. Ces outils sont payants, mais croyez-moi, ils valent la dépense grâce à ce que vous allez économiser.
Sinon les outils de la JVM HotSpot de SUN Microsystems permettent aussi de regarder ce qu’il se passe à l’intérieur de l’application, et de corriger le tout.
Dans la série des autres scanners que j’utilise, je conseille FindBugs pour auditer le code source, et voir les bugs éventuels de l’application. Si vous voulez aller plus loin, vous pouvez installer et utiliser Sonar, j’en parle au point 6.
Un outil comme PMD est aussi gratuit et open-source. Il permet d’auditer l’ensemble du code, de signaler le code mort qui n’est jamais appelé, de vous informer des duplications et d’éventuels bugs.
Enfin je conseille aussi de nettoyer les classes : effacez le code en commentaire. De ma propre expérience, 9 fois sur 10 lorsque je décommente du code dans une classe, il ne compile plus. Le reste de la classe a changé, ou il ne sert à rien. Soyez sans pitié : effacez le code et utilisez le gestionnaire de version pour sauver vos anciennes versions. Rien ne se perd de cette façon.
Pensez aussi à nettoyer les dépendances Maven ou Ant, et à vous assurer que vous avez réellement besoin de toutes ces librairies. Avec Java 6 les choses ont changé, prenez le temps de nettoyer le code puis d’effacer les anciennes librairies.
Règle 6 : mettez en place un suivi
Une fois votre application modernisée, ou plutôt avant même d’y avoir touché, mettez en place des outils de suivi de la qualité du code, de la couverture des tests, de la performance en mémoire et CPU… Bref installez une Tour de contrôle avant de faire de l’archéologie.
Sans hésiter, installez Sonar afin de mettre votre dette technique sous contrôle. Il demande un peu d’efforts pour être correctement réglé, mais ce que j’aime beaucoup c’est le suivi. Il vous donnera un indicateur de progrès. Et il sera là aussi pour signaler aux développeurs qu’il surveille la qualité de votre produit.
Donc mettre en place un suivi du patient, afin de s’assurer qu’il ne revienne pas sur la table d’opération toutes les 3 semaines.
Règle 7 : pensez au futur
J’ai en tête la célèbre phrase du wiki C2 sur la page Code for the Maintainer :
« Always code as if the person who ends up maintaining your code is a violent psychopath who knows where you live. »
Ce qui donne
« Code toujours comme si tu savais que la personne qui maintiendra ton code est un psychopathe violent qui sait où tu habites… »
En fait il faut être eco-responsable. Vous faîtes du tri sélectif ? Et bien codez proprement. Pensez que ce sera peut-être vous dans 6 ans qui serez appelé pour corriger un problème sur votre logiciel. Ou peut-être votre fils qui aura trouvé votre nom sur Google dans un projet open-source Lambda dans 15 ans… Bref codez pour être fier de montrer votre programme à votre fils dans quelques années.
Conclusion
Je me demande si les projets et les applications passent la barre des 10 ans dans le monde Java. Je ne parle pas d’autres langages, je parle que de Java. Avec les projets ou les produits sur lesquels j’ai travaillé, j’ai déjà la réponse. Je sais ce qu’ils sont devenus. J’ai eu par contre à reprendre du code de nombreuses fois, et à faire ce travail de mise à jour, sans tout casser. Il y a donc une compétence « Docteur Love, maître du Lifting » à acquérir pour faire un bon développeur dans le monde Java.
For those of you planning to start a new project with the Visual Studio Scrum 1.0 Process Template, Mickey Gousset just write a good paper in the Visual Studio Magazine. He introduces the new Microsoft Scrum 1.0 Process Template for Visual Studio Team Foundation Server and steps through the installation process.
Urban Turtle 3.2 fully supports the new Visual Studio Scrum 1.0 Process Template. Using the index card metaphor with drag-and-drop functionality, it is the perfect replacement for Excel-based planning workbooks.
So I'm back from FISL and I have to say I wasn't disappointed.
For people that have attended both FISL and OSCON these two conferences seem to have a lot in common. FISL had somewhere between 4000 and 5000 attendees interested in many different OSS topics which means it's quite different from your typical Java conference (lots of python, multimedia, security, linux desktops, etc.).
Coming in I spotted a competition to win an iPad and thought to myself "good, I might get away with using a Mac on stage" but this has to be one of the conferences with the least number of Apple machines, netbooks being *very* popular.
I had two talks, one called "GlassFish OSGi - from modular runtime to hybrid applications" and another titled "The future of the GlassFish community" (check the links for the slide decks). The format for the conference is a hard-stop after 40-minutes to leave time for 10-minutes of Q&A which made it a bit challenging to deliver all the content I had prepared but I certainly got a number of good questions (Brazilians are not shy ;).
It was great to meet again with Fabiane and Bruno and chat about the Sun past and the Oracle future. They are doing a lot for the Java community in Brazil (check out "Javali" coming out soon) as well as their new baby, ToolsCloud.com. So other than the nice people, the tasty meat, learning about chimarrãos, I also found out that a Frenchmen's Belgian (or Swiss) jokes are a Brazilian's jokes on Portuguese.
Soyons décontracté pendant l’exercice qui va suivre. Dire du bien ou dire du mal, c’est facile. On peut passer des heures sur des blogs à s’envoyer des commentaires à la figure, ou sinon on peut prendre son temps pour regarder les choses. Moi vous voyez, je suis un gars avec un esprit ouvert. Il y a 3 ou 4 ans je me serai coupé un bras pour vous faire apprendre JSF à la place de Struts. Je me suis ensuite coupé un deuxième bras pour vous parler de Seam et de la gestion de la conversation, par rapport aux soucis des premiers frameworks Webs. Je vous ai bassiné les oreilles avec Grails l’hiver dernier, et en ce moment je ne vous parle que de Play! Framework. Donc dans la logique des choses, je devrai tomber amoureux d’un nouveau framework PouetPouet d’ici quelques semaines. En attendant, et là je me sens à l’aise dans mes baskets, je peux vous parler de ce que j’observe. Quand je dis « JE » cela veut dire que j’ai eu le temps et l’occasion de tester ce que je vois sur de vrais projets.
Il n’y a pas de mauvais frameworks Webs. Selon votre projet je vous encourage à tester différentes approches et à évoluer selon vos besoins fonctionnels. Essayez aussi de prototyper avec un outil comme Balsamiq ou iRise Studio par exemple.
Pensez incrément lorsque vous faîtes du Web. Essayez de construire par petites étapes votre solution, et faites valider par l’utilisateur final les écrans. Le plus important : un logiciel qui marche et une interface qui répond à la demande du client. C’est cela qui doit vous guider pendant votre développement.
And now… JSF 2 and Play! FrameworkSi vous souhaitez suivre le code de cet article, je vous encourage à suivre le petit tutorial que j’ai écris sur NetBeans 6.9. Cela vous permettra de générer rapidement une petite application en JSF 2 et Java EE 6 pour la partie Bean. Vous pouvez télécharger le code complet de la partie JSF ici afin de pouvoir suivre simplement la discusssion.
Pour écrire cet article, j’ai écris la partie Play! Framework après la partie JSF 2.0. Je vous guiderai donc sur la partie Play! Framework.
Création d’une application avec Play! Framework
- Télechargez Play! Framework sur le site de Play!
- Décompressez l’archive, ajoutez ensuite le répertoire de play à votre PATH.
- Ouvrez un terminal, dans un répertoire de test, tapez « play » afin de vérifier qu’il est bien installé. J’ai utilisé la version 1.0.3
macbook-pro-de-nicolas-martignole:NetBeansProjects nicolas$ play
~ _ _
~ _ __ | | __ _ _ _| |
~ | '_ | |/ _' | || |_|
~ | __/|_|____|__ (_)
~ |_| |__/
~
~ play! 1.0.3, http://www.playframework.org
~ framework ID is macbookpro
~
~ Usage: play cmd [app_path] [--options]
~
~ with, new Create a new application
~ run Run the application in the current shell
~ help Show play help
~
- Tapez ensuite « play new micromarket » afin de créer une application de test
macbook-pro-de-nicolas-martignole:NetBeansProjects nicolas$ play new micromarket
~ _ _
~ _ __ | | __ _ _ _| |
~ | '_ | |/ _' | || |_|
~ | __/|_|____|__ (_)
~ |_| |__/
~
~ play! 1.0.3, http://www.playframework.org
~ framework ID is macbookpro
~
~ The new application will be created in /Users/nicolas/NetBeansProjects/micromarket
~ What is the application name? micromarket
~
~ OK, the application is created.
~ Start it with : play run micromarket
~ Have fun!
~
macbook-pro-de-nicolas-martignole:NetBeansProjects nicolas$
- Placez vous dans le répertoire micromarket puis tapez play run et vérifiez que la page de démarrage de Play! se lance sur [localhost:9000]
Bravo, vous avez créé une application Play.
Entité JPA
Vous pouvez prendre l’entité JPA MicroMarket du projet NetBeans et la placer dans le répertoire de Play!, cela fonctionne. En effet, il utilise JPA et Hibernate, donc pour lui, cela ne change pas grand chose. Voici le code initial de l’entité MicroMarket avant modification :
package org.letouilleur.demo;
import java.io.Serializable;
import javax.persistence.Basic;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.Id;
import javax.persistence.NamedQueries;
import javax.persistence.NamedQuery;
import javax.persistence.Table;
/**
*
* @author nicolas
*/
@Entity
@Table(name = "MICRO_MARKET")
@NamedQueries({
@NamedQuery(name = "MicroMarket.findAll", query = "SELECT m FROM MicroMarket m"),
@NamedQuery(name = "MicroMarket.findByZipCode", query = "SELECT m FROM MicroMarket m WHERE m.zipCode = :zipCode"),
@NamedQuery(name = "MicroMarket.findByRadius", query = "SELECT m FROM MicroMarket m WHERE m.radius = :radius"),
@NamedQuery(name = "MicroMarket.findByAreaLength", query = "SELECT m FROM MicroMarket m WHERE m.areaLength = :areaLength"),
@NamedQuery(name = "MicroMarket.findByAreaWidth", query = "SELECT m FROM MicroMarket m WHERE m.areaWidth = :areaWidth")})
public class MicroMarket implements Serializable {
private static final long serialVersionUID = 1L;
@Id
@Basic(optional = false)
@Column(name = "ZIP_CODE")
private String zipCode;
@Column(name = "RADIUS")
private Double radius;
@Column(name = "AREA_LENGTH")
private Double areaLength;
@Column(name = "AREA_WIDTH")
private Double areaWidth;
public MicroMarket() {
}
public MicroMarket(String zipCode) {
this.zipCode = zipCode;
}
public String getZipCode() {
return zipCode;
}
public void setZipCode(String zipCode) {
this.zipCode = zipCode;
}
public Double getRadius() {
return radius;
}
public void setRadius(Double radius) {
this.radius = radius;
}
public Double getAreaLength() {
return areaLength;
}
public void setAreaLength(Double areaLength) {
this.areaLength = areaLength;
}
public Double getAreaWidth() {
return areaWidth;
}
public void setAreaWidth(Double areaWidth) {
this.areaWidth = areaWidth;
}
@Override
public int hashCode() {
int hash = 0;
hash += (zipCode != null ? zipCode.hashCode() : 0);
return hash;
}
@Override
public boolean equals(Object object) {
// TODO: Warning - this method won't work in the case the id fields are not set
if (!(object instanceof MicroMarket)) {
return false;
}
MicroMarket other = (MicroMarket) object;
if ((this.zipCode == null && other.zipCode != null) || (this.zipCode != null && !this.zipCode.equals(other.zipCode))) {
return false;
}
return true;
}
@Override
public String toString() {
return "org.letouilleur.demo.MicroMarket[zipCode=" + zipCode + "]";
}
}
A cet instant, je vais vous proposer une approche un peu différente. J’insiste lourdement sur le fait que ce n’est pas obligatoire mais intéressant. Play! propose d’utiliser une approche orientée DDD où les objets du domaine ne sont pas anémiques, mais capables d’effectuer des services simples. Pour cela, le pattern que j’utilise est de donner une référence à ce que l’on appelle le « Repository » dans DDD, et de l’injecter dans mon Entité. Grâce à une classe générique, dans laquelle l’EntityManager sera injecté, et dans laquelle je vais déclarer mes méthodes standards type CRUD, toutes mes Entités pourront alors exécuter du code d’accès au Repository.
Ce que nous allons voir n’est pas mieux ou moins bien que l’approche services/par couche que vous connaissez tous. J’ai envie de vous dire de laisser tomber les couches et de grandir pour faire un bon mot.
Play! propose (n’impose pas) d’étendre soit la classe JPASupport, soit la classe Model, afin que l’accès à l’EntityManager soit injecté par le framework. C’est un couplage fort, mais sur la modélisation du domaine ce n’est pas un souci. Je n’ai pas vu de projets avec de l’héritage d’Entité, et j’ai souvent vu que les problèmes de composition ou d’agrégation étaient résolus sans faire appel à de l’héritage de classes.
J’effectue d’abord 3 modifications : le package, un import en plus et l’extends :
// Fichier micromarket/app/models/org/letouilleur/demo/MicroMarket.java
// dans le répertoire de l'appli Play!
// Changement 1
package models.org.letouilleur.demo;
import java.io.Serializable;
import javax.persistence.Basic;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.Id;
import javax.persistence.NamedQueries;
import javax.persistence.NamedQuery;
import javax.persistence.Table;
// Changement 2
import play.db.jpa.*;
@Entity
@Table(name = "MICRO_MARKET")
@NamedQueries({
@NamedQuery(name = "MicroMarket.findAll", query = "SELECT m FROM MicroMarket m"),
@NamedQuery(name = "MicroMarket.findByZipCode", query = "SELECT m FROM MicroMarket m WHERE m.zipCode = :zipCode"),
@NamedQuery(name = "MicroMarket.findByRadius", query = "SELECT m FROM MicroMarket m WHERE m.radius = :radius"),
@NamedQuery(name = "MicroMarket.findByAreaLength", query = "SELECT m FROM MicroMarket m WHERE m.areaLength = :areaLength"),
@NamedQuery(name = "MicroMarket.findByAreaWidth", query = "SELECT m FROM MicroMarket m WHERE m.areaWidth = :areaWidth")})
// Changement 3
public class MicroMarket extends JPASupport implements Serializable {
private static final long serialVersionUID = 1L;
@Id
@Basic(optional = false)
@Column(name = "ZIP_CODE")
private String zipCode;
@Column(name = "RADIUS")
private Double radius;
@Column(name = "AREA_LENGTH")
private Double areaLength;
@Column(name = "AREA_WIDTH")
private Double areaWidth;
public MicroMarket() {
}
public MicroMarket(String zipCode) {
this.zipCode = zipCode;
}
public String getZipCode() {
return zipCode;
}
public void setZipCode(String zipCode) {
this.zipCode = zipCode;
}
public Double getRadius() {
return radius;
}
public void setRadius(Double radius) {
this.radius = radius;
}
public Double getAreaLength() {
return areaLength;
}
public void setAreaLength(Double areaLength) {
this.areaLength = areaLength;
}
public Double getAreaWidth() {
return areaWidth;
}
public void setAreaWidth(Double areaWidth) {
this.areaWidth = areaWidth;
}
@Override
public int hashCode() {
int hash = 0;
hash += (zipCode != null ? zipCode.hashCode() : 0);
return hash;
}
@Override
public boolean equals(Object object) {
// TODO: Warning - this method won't work in the case the id fields are not set
if (!(object instanceof MicroMarket)) {
return false;
}
MicroMarket other = (MicroMarket) object;
if ((this.zipCode == null && other.zipCode != null) || (this.zipCode != null && !this.zipCode.equals(other.zipCode))) {
return false;
}
return true;
}
@Override
public String toString() {
return "org.letouilleur.demo.MicroMarket[zipCode=" + zipCode + "]";
}
}
Ensuite, éditez le fichier micromarket/conf/application.conf de Play! afin d’activer une base de données. Nous allons utiliser une base en mémoire. Pas besoin de s’embêter avec le schéma, car Play! va créer une table MicroMarket identique à notre spécification, sans même avoir besoin de relancer le serveur.
A cet instant, si vous sauvez et que vous rechargez votre page, Play! compile le code, et vous serez notifié d’éventuelles erreurs. Pour l’instant nous n’avons pas d’entrées dans notre base, ni de page. Il faut continuer.
Allons encore plus loin. Les NamesQuery sont utilisables, mais je vais utiliser des fonctions de Play! afin d’avoir le support de la pagination intégré, sans devoir écrire cette logique dans une autre classe. Cela équivaut au code utilisant PaginationHelper de la classe MicroMarketController. Je n’invente rien. Avec ce que je vous montre nous n’avons pas besoin de déclarer ce système de PaginationHelper comme dans la version JSF, puisque c’est Play! qui vous le donne. Deuxième modification : je passe en public les attributs, puisque les getters/setters ne me servent à rien ici.
L’entité au final est donc encore plus simple :
package models.org.letouilleur.demo;
import java.io.Serializable;
import javax.persistence.Basic;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.Id;
import javax.persistence.Table;
import play.db.jpa.*;
import java.util.*;
@Entity
@Table(name = "MICRO_MARKET")
public class MicroMarket extends JPASupport implements Serializable {
private static final long serialVersionUID = 1L;
@Id
@Basic(optional = false)
@Column(name = "ZIP_CODE")
public String zipCode;
@Column(name = "RADIUS")
public Double radius;
@Column(name = "AREA_LENGTH")
public Double areaLength;
@Column(name = "AREA_WIDTH")
public Double areaWidth;
public MicroMarket() {
}
public MicroMarket(String zipCode) {
this.zipCode = zipCode;
}
@Override
public int hashCode() {
int hash = 0;
hash += (zipCode != null ? zipCode.hashCode() : 0);
return hash;
}
@Override
public boolean equals(Object object) {
// TODO: Warning - this method won't work in the case the id fields are not set
if (!(object instanceof MicroMarket)) {
return false;
}
MicroMarket other = (MicroMarket) object;
if ((this.zipCode == null && other.zipCode != null) || (this.zipCode != null && !this.zipCode.equals(other.zipCode))) {
return false;
}
return true;
}
@Override
public String toString() {
return "org.letouilleur.demo.MicroMarket[zipCode=" + zipCode + "]";
}
/** Retourne la liste des MicroMarket en utilisant la méthode find hérité de JPASupport
* zipCode : critere de recherche
* page: numéro de la page à charger, si vous affichez un tableau paginé par exemple
* length: taille de la page, par exemple 10 pour charger 10 entrées
*/
public List<Micromarket> findByZipCode(String zipCode, int page, int length) {
return find("from MicroMarket m where m.zipcode like :p").bind("p",zipCode).fetch(page,length);
}
//.. autre finder
}
Voilà, comme vous pouvez le constater, ce n’est pas une révolution, simplement une approche différente pour le domaine.
La partie Java EE 6Pour commencer, je vais regarder comment fonctionne la page qui donne la liste des Entités, avec la pagination. Dans la version JSF, nous avons un ManagedBean, la class MicroMarketController. Celui-ci utilise un EJB Stateless, MicroMarketFacade, qui donne accès à l’EntityManager, un DAO en quelque sorte.
public abstract class AbstractFacade<T> {
private Class<T> entityClass;
public AbstractFacade(Class<T> entityClass) {
this.entityClass = entityClass;
}
protected abstract EntityManager getEntityManager();
public void create(T entity) {
getEntityManager().persist(entity);
}
public void edit(T entity) {
getEntityManager().merge(entity);
}
public void remove(T entity) {
getEntityManager().remove(getEntityManager().merge(entity));
}
public T find(Object id) {
return getEntityManager().find(entityClass, id);
}
public List<T> findAll() {
javax.persistence.criteria.CriteriaQuery cq = getEntityManager().getCriteriaBuilder().createQuery();
cq.select(cq.from(entityClass));
return getEntityManager().createQuery(cq).getResultList();
}
public List<T> findRange(int[] range) {
javax.persistence.criteria.CriteriaQuery cq = getEntityManager().getCriteriaBuilder().createQuery();
cq.select(cq.from(entityClass));
javax.persistence.Query q = getEntityManager().createQuery(cq);
q.setMaxResults(range[1] - range[0]);
q.setFirstResult(range[0]);
return q.getResultList();
}
public int count() {
javax.persistence.criteria.CriteriaQuery cq = getEntityManager().getCriteriaBuilder().createQuery();
javax.persistence.criteria.Root<T> rt = cq.from(entityClass);
cq.select(getEntityManager().getCriteriaBuilder().count(rt));
javax.persistence.Query q = getEntityManager().createQuery(cq);
return ((Long) q.getSingleResult()).intValue();
}
}
Sa spécialisation en classe MicroMarketFacade est super simple et propre :
package org.letouilleur.demo;
import javax.ejb.Stateless;
import javax.persistence.EntityManager;
import javax.persistence.PersistenceContext;
@Stateless
public class MicroMarketFacade extends AbstractFacade<Micromarket> {
@PersistenceContext(unitName = "WebApplication1PU")
private EntityManager em;
protected EntityManager getEntityManager() {
return em;
}
public MicroMarketFacade() {
super(MicroMarket.class);
}
}
Jusqu’ici, on voit qu’avec 2 classes et Java EE 6 vous pouvez faire l’équivalent de ce que vous avez l’habitude de faire avec Spring par exemple. Je trouve le code simple et efficace.
JSF et la partie ManagedBeanAttention là c’est un poil plus compliqué. La classe MicroMarketController est un ManagedBean JSF, capable de gérer la pagination. Le moins que l’on puisse dire, c’est que ce n’est pas simple.
Pour être plus équitable, j’ai retiré la pagination, et l’on voit alors que le code est simple, pas plus compliqué que la version Play! Framework. La version originale est plus complexe, avec la gestion de la pagination et un Converter pour retenir l’élément sélectionné. S’il y a possibilité de critiquer JSF, c’est sur cette partie, que je vous laisse regarder dans le fichier original.
package org.letouilleur.demo;
import javax.ejb.EJB;
import javax.faces.bean.ManagedBean;
import javax.faces.bean.SessionScoped;
import javax.faces.component.UIComponent;
import javax.faces.context.FacesContext;
import javax.faces.convert.Converter;
import javax.faces.convert.FacesConverter;
import javax.faces.model.DataModel;
import javax.faces.model.ListDataModel;
@ManagedBean (name="microMarketController")
@SessionScoped
public class MicroMarketController {
private DataModel items = null;
@EJB private org.letouilleur.demo.MicroMarketFacade ejbFacade;
public MicroMarketController() {
}
private MicroMarketFacade getFacade() {
return ejbFacade;
}
public String prepareList() {
recreateModel();
return "List";
}
public DataModel getItems() {
if (items == null) {
items = new ListDataModel(getFacade().findAll());
}
return items;
}
private void recreateModel() {
items = null;
}
}
Et si nous écrivions le controleur de la version Play ? Vous êtes prêt ?
Ouvrez la class app/controllers/Application.java et ajouter une nouvelle méthode static list comme ci-dessous:
// CODE PLAY dans app/controllers/Application.java
package controllers;
import play.mvc.*;
import models.org.letouilleur.demo.MicroMarket;
import java.util.List;
public class Application extends Controller {
public static void index() {
render();
}
public static void list() {
List listOfMicroMarkets=MicroMarket.findAll();
render(listOfMicroMarkets);
}
}
Voilà, c’est tout.
La vueSi vous le voulez bien, terminons par la vue. Pour Play c’est assez simple, voir un peu simpliste. Créez une page list.html dans le répertoire views/Application. La page portant le même nom que la méthode du contrôleur, elle sera chargée automatiquement par Play!
#{extends 'main.html' /}
#{set title:'List of MicroMarket' /}
List of MicroMarket
#{list items:listOfMicroMarkets, as:'currentMicroMarket'}
${currentMicroMarket.id} ${currentMicroMarket.zipCode} ${currentMicroMarket.radius} ${currentMicroMarket.areaLength} ${currentMicroMarket.areaWidth}
#{/list}
Si vous sauvez cette page et que vous testez… petit déception. Il n’y a rien dans notre base de données. Comment tester ? Et bien nous allons créer un Bootstrap (comme avec Grails) afin de charger un fichier YAML. Ce fichier contiendra 3 entrées pour tester.
Créez un fichier BootStrap.java dans le répertoire app de Play:
import play.Play;
import play.jobs.*;
import play.test.*;
import models.*;
import models.org.letouilleur.demo.*;
@OnApplicationStart
public class BootStrap extends Job {
public void doJob() {
if (Play.mode == Play.Mode.DEV) {
Fixtures.load("test-data.yml");
}
}
}
Ensuite créez un fichier test-data.yml dans le répertoire conf:
models.org.letouilleur.demo.MicroMarket(m1):
zipCode: 95051.0
radius: 255.59
areaLength: 689.856
areaWidth: 478.479
models.org.letouilleur.demo.MicroMarket(m2):
zipCode: 33740.0
radius: 12.59
areaLength: 389.52
areaWidth: 528.41
models.org.letouilleur.demo.MicroMarket(m3):
zipCode: 89211.0
radius: 120.3
areaLength: 39.856
areaWidth: 452.52
Relancez le serveur, et chargez alors la page pour lister nos entrées ([localhost:9000]). Hop voilà la liste des entrées de la base. Notez que la page est moche par rapport à la version JSF.

La version JSF est plus complète, mais on va revenir là dessus à la fin:

Et pour terminer, regardons le code la partie JSF, dans le fichier List.xhtml. J’ai retiré la pagination afin d’alléger la page :
<?xml version="1.0" encoding="ISO-8859-1" ?>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml"
xmlns:ui="http://java.sun.com/jsf/facelets"
xmlns:h="http://java.sun.com/jsf/html"
xmlns:f="http://java.sun.com/jsf/core">
<ui:composition template="/template.xhtml">
<ui:define name="title">
<h:outputText value="#{bundle.ListMicroMarketTitle}"></h:outputText>
</ui:define>
<ui:define name="body">
<h:form styleClass="jsfcrud_list_form">
<h:panelGroup id="messagePanel" layout="block">
<h:messages errorStyle="color: red" infoStyle="color: green" layout="table"/>
</h:panelGroup>
<h:outputText escape="false" value="#{bundle.ListMicroMarketEmpty}" rendered="#{microMarketController.items.rowCount == 0}"/>
<h:panelGroup rendered="#{microMarketController.items.rowCount > 0}">
<h:dataTable value="#{microMarketController.items}" var="item" border="0" cellpadding="2" cellspacing="0" rowClasses="jsfcrud_odd_row,jsfcrud_even_row" rules="all" style="border:solid 1px">
<h:column>
<f:facet name="header">
<h:outputText value="#{bundle.ListMicroMarketTitle_zipCode}"/>
</f:facet>
<h:outputText value="#{item.zipCode}"/>
</h:column>
<h:column>
<f:facet name="header">
<h:outputText value="#{bundle.ListMicroMarketTitle_radius}"/>
</f:facet>
<h:outputText value="#{item.radius}"/>
</h:column>
<h:column>
<f:facet name="header">
<h:outputText value="#{bundle.ListMicroMarketTitle_areaLength}"/>
</f:facet>
<h:outputText value="#{item.areaLength}"/>
</h:column>
<h:column>
<f:facet name="header">
<h:outputText value="#{bundle.ListMicroMarketTitle_areaWidth}"/>
</f:facet>
<h:outputText value="#{item.areaWidth}"/>
</h:column>
<h:column>
<f:facet name="header">
<h:outputText value=" "/>
</f:facet>
</h:column>
</h:dataTable>
</h:panelGroup>
<br />
</h:form>
</ui:define>
</ui:composition>
</html>
Alors pour enfoncer le clou, je propose de piquer la CSS de JSF, de mettre un tableau HTML simple, et de voir ce que cela donne. Play! Framework utilise le langage Groovy comme moteur de template dans la vue. Cela rend plus simple l’écriture des pages, mais ne permet pas de se faire aider lors de la création des pages, comme avec JSF dans NetBeans. Il y a donc un risque d’erreur. La bonne nouvelle, c’est que vous n’avez pas à arrêter Play! pour voir vos modifications. Il suffit de recharger la page dans votre navigateur.
Et le code HTML associé :
#{extends 'main.html' /}
#{set title:'List of MicroMarket' /}
<h2>List of MicroMarket</h2>
<P>Play! Framework</P>
<table border="0" cellpadding="2" cellspacing="0" rules="all" style="border:solid 1px">
<thead>
<tr class="">
<th scope="col">ZipCode</th>
<th scope="col">Radius</th>
<th scope="col">AreaLength</th>
<th scope="col">AreaWidth</th>
</tr>
</thead>
<tbody>
#{list items:listOfMicroMarkets, as:'currentMicroMarket'}
<tr class="jsfcrud_${currentMicroMarket_parity}_row">
<td>${currentMicroMarket.zipCode}</td>
<td>${currentMicroMarket.radius}</td>
<td>${currentMicroMarket.areaLength}</td>
<td>${currentMicroMarket.areaWidth}</td>
</tr>
#{/list}
</tbody>
</table>
Analyse des 2 approches
Que pensez-vous de la partie vue de la version JSF ? Objectivement, j’ai l’impression d’écrire en tag JSF du code HTML. Je sais la puissance de JSF et que je peux gagner du temps, mais au prix d’une complexité dont je n’ai pas besoin dès le départ. Côté Play! Framework, les tableaux paginés et triés existent aussi, grâce aux librairies de jQuery. D’ailleurs, le code généré par JSF utilise aussi la librairie jQuery. Au final cela ne change pas grand chose dans le navigateur. Donc je préfère la simplicité de Play! Framework à l’approche complète de JSF, mais il faut reconnaître que JSF sera en mesure de construire des interfaces plus riches que Play!, qui reste très basique.
Côté serveur, la partie Entité et DAO ne change pas beaucoup. La philosophie est différente, mais cela correspond à une manière de coder. Le ManagedBean et son Converter dans la version complète est lui aussi un peu compliqué. Pourquoi avoir un DataModel (regardez le code de la méthode List) alors qu’une List d’entité ferait l’affaire ?
ConclusionAprès avoir vu tout ce code, on voit bien la différence d’approche. Il n’y a pas de vainqueur, il y a deux approches différentes. Côté Model et Controleur, c’est du pareil au même. A part l’approche orientée DDD de Play! nous n’avons pas vu de grosses différences. Côté vue, j’ai le choix entre JSF qui me permet d’écrire plutôt une application type client riche, ou Play! qui me laisse me débrouiller avec ma page HTML.
La conclusion ?
C’est parce que Play! Framework est proche de ce que nous avons déjà l’habitude de faire que je vous recommande de regarder. Il vous faudra quelques heures à peine pour vous en servir, et vous serez surpris. Productivité, plaisir et impression d’avancer.
Références:
Le code du projet Play! zippé et du projet JSF sont disponible ici: [touilleur-express.fr].
Chapitre 2
Mon éditeur (Dunod) m'a demandé de commencer à réfléchir à une deuxième édition de Scrum, le guide pratique de la méthode agile la plus populaire. Oh, on a le temps, c'est pour une publication au printemps 2011. Une 2ème édition peut inclure un éventuel nouveau chapitre, mais le plus usuel est d'améliorer et d'ajouter des paragraphes à ceux... Lire Des sprints pour une release

ASP.NET MVC est une plateforme qui a été introduite par Microsoft essentiellement pour augmenter la testabilité des applications Web.
En ASP.NET traditionnel, il est relativement complexe de tester unitairement la logique à l’intérieur des contrôles.
Le cycle évènementiel étant difficile à simuler, il devient nécessaire d’utiliser des ‘fakes’ ou ’stubs’ des classes partielles des contrôles individuels, ce qui génère beaucoup de code pour de simples tests unitaires. De plus, la nécessité de ces ’stubs’ complexifie passablement la pratique du développement piloté par les tests (TDD).
En ASP.NET MVC, c’est la séparation en trois responsabilités distinctes (modèle, vue et contrôleur) qui augmente significativement la testabilité des systèmes développés en MVC comparativement à la plateforme populaire ASP.NET WebForms. Ainsi, toute la logique nécessaire à l’exécution des actions appelées par un groupe de vues se trouve à l’intérieur d’un seul contrôleur. Il devient donc relativement simple de tester unitairement, directement (sans ’stub’ de contrôle) et avec un minimum de code la logique métier des actions exécutables pour un contrôleur donné. On peut ainsi plus facilement piloter le développement du comportement de nos actions en le réfléchissant à l’intérieur d’un test avant même d’en écrire le code! On peut même aller jusqu’à tester les messages d’erreur qui peuvent être retournés aux vues selon les différentes actions. Bien sûr, il est toujours nécessaire de créer des ‘fakes’ pour les objets du domaine dont la logique interne est testée dans d’autres tests unitaires.
En plus d’être un moyen d’assurer la qualité du logiciel, les tests unitaires sont aussi une excellente source de documentation du code. Il est donc intéressant d’utiliser une syntaxe permettant à n’importe quel utilisateur ou programmeur qui ne connaît pas encore le système de comprendre le comportement du système.
Voici une proposition pour nommer les tests et les classes de tests afin d’augmenter leur lisibilité :
Un exemple de code avec MS Test :
[TestClass]
public class WhenClosingTheBlog
{
[TestMethod]
public void UserShouldSeeAWarningMessageIfHeHasNotSavedHisBlog()
{
var blogController = BlogControllerFactory.Generate();
var actionResult = blogController.Save(new Blog()) as
RedirectToRouteResult;
Assert.AreEqual(BlogController.PENDING_CHANGE_HAS_NOT_BEEN_SAVED
, blogController.TempData["WarningMessage"] as string);
Assert.AreEqual("Warning", actionResult.RouteValues["action"]);
Assert.AreEqual("Blog", actionResult.RouteValues["controller"]);
}
}
public class BlogController : Controller
{
private public const string PENDING_CHANGE_HAS_NOT_BEEN_SAVED =
"Vous avez actuellement des changements non sauvegardé. Êtes vous sur de vouloir quitter la page ?";
//...
[HttpPost]
public ActionResult Save(Blog currentBlog)
{
var path = userProfile.Path;
if (currentBlog.HasPendingChange)
{
TempData["WarningMessage"] =
PENDING_CHANGE_HAS_NOT_BEEN_SAVED;
return View("Warning");
}
return RedirectToAction("Home");
}
//...
}
Cette testabilité est un aspect important qui peut justifier l’utilisation d’ASP.NET MVC dans un contexte de développement d’un projet Web nécessitant l’utilisation de pratiques d’ingénierie Agiles.
There's some very interesting follow-up to a blog entry called If you’re an architect… on Edward Williams' blog and one of the questions that has just been posed is this.
Is defining the development process (agile, waterfall - XP/Scrum/DSDM/Lean etc) the responsibility of the technical architect or does it come under the enterprise architect / CIO / CTO's umbrella?
This may sound controversial, but I've met very few IT managers (project managers included) who understand that software doesn't have to be developed according to the waterfall model, and fewer still that have actively shaped projects to take advantage of those other approaches.
It's a really interesting question. As an architect on a software project, the overall development process is always something that *I* try to have some influence over. My reasons for this are (a) I've seen too many waterfall style projects fail and (b) you can use iterative and incremental approaches to help with many of the challenges you face as an architect. For example, rather than get freaked out by some complex requirements and head into analysis paralysis, why not structure the project in iterations and tackle those things first, perhaps by doing some lightweight architecture/design and writing some code that you can load test.
To be honest, I usually get involved in shaping the overall project because nobody else has considered it and the project is bumbling along in a fairly ad hoc, waterfall-ish fashion. I like to inject some structure into the way that software is built and the architectural drivers can really help to define what that structure should be. There are certainly some challenges to be overcome once you start getting involved in shaping the overall process though. These range from project managers that "just want a normal project plan" through to those that agree with your use of timeboxes but "still just want a normal project plan". These sorts of issues are relatively easy to overcome, but I do often find myself doing some just-in-time training about the different types of development process.
Should architects define the software development process? Who defines the software process on your projects?

So you have initiated an Agile transition and have faced some resistance to change! Or maybe, you assessed your current level of Agile Maturity and are hoping to achieve the next level. Better yet, you and your team are planning to launch an Agile transition that is not driven by the wrong reasons.
That’s great!
If you haven’t already done so, you may want to read: Getting Started – Reference Material for Managers Who Wish to Understand Agile and Scrum and What consultants don’t tell you before you begin an agile transition.
Let’s cut the chase and get to the point. Are you ready? Here it is. The secret to a successful Agile Transition -> Make people look good!
Yes. That’s it. Surprised?
I’m not talking about psychological manipulation. I’m talking about finding what drives the people you are working with and the managers around them and then capitalize on their drivers in order to get them to get on board with the transition – and better yet become evangelist for your transition. Here are some examples:
Don’t get me wrong. I am not asking you to lie, to cheat or to fake the objectives and expected outcome. I’m telling you to get others on board and working WITH you by telling them the whole story and helping them understand that there is something in it for THEM too.
Agile relies heavily on communications and interactions. Why don’t you start with all the people directly and indirectly impacted by the transition? Sure, it will require more time in the short term to influence people into supporting you but in the long run, you will be glad you did it.
Go ahead. Try to figure out what drives people around you or what issues they are facing. Find a solution that can help them and you’ll end-up with a win-win scenario and a successful transition.
You might be interested in these related posts: