
Je me suis rendu à la FOSDEM pour la première fois cette année et j’en reviens enchanté.
Cette conférence anglophone, ayant lieu chaque année à Bruxelles pendant un week-end en début d’année, est conséquente : plus de 5000 participants et 250 présentations réparties dans plusieurs bâtiments d’un campus universitaire de l’Université Libre de Bruxelles.
De nombreuses sessions m’ont laissé une bonne impression, j’en détaille ici quelques unes.
Lire la suite »
Bonjour,
Vous avez remarqué, on est à fond dans l’itératif. Lors de mon dernier projet, au démarrage, l’idée de faire évoluer la base de données au fur à mesure de la “découverte” des besoins n’allait pas de soit. La base de données est quelque chose de tellement rigide, il faut tout définir dés le départ. Vous savez que ce n’est pas vraiment le genre de la maison. Néanmoins, comment adresser ce point ?
Lire la suite »
La deuxième journée de conférence a démarré avec une keynote animé par JBoss et Google:
Keynote
La première partie de la Keynote a été animé par l’équipe JBoss qui a annoncé le changement de nom et invite les utilisateurs à voter.
La deuxième partie de la keynote était consacré à l’équipe Google. Tim Bray a invité à être en ligne avec une superbe vidéo présentant le périple et destination sur Street View. Ensuite, il a présenté les derniers produits de Google: le nouveau téléphone Nexus 4 et la nouvelle tablette Nexus 10.


Romain Guy a présenté les outils de développement pour Android que vous pouvez trouver sur leur site.Il a aussi donné le nombres des périphériques Android dans le monde qui s’élève à 560 Millions, l’équivalent de deux fois et demi la distance entre la terre et la lune.

Ludovic Champenois nous a ensuite présenté Google Compute Engine, la nouvelle plate-forme de Cloud de Google. Le Google Compute Engine fournit des machines Linux virtuelles pour héberger des applications scalables. Amazon n’a qu’à bien se tenir car le produit Google est très ambitieux. Tim Bray a repris la parole ensuite pour parler de la sécurité sur le web et inviter à assister à la présentation de OAuth2 et Identity.
Lire la suite »
J’ai eu la chance cette année d’assister aux journées conférences de la 11ème édition de Devoxx à Anvers. L’événement s’est déroulé du 12 au 16 Novembre et plusieurs nouveautés techniques étaient au rendez-vous.
Keynote
La première journée de conférence a commencé par une keynote très « robotique »: on a assisté à une chorégraphie de cinq robots Nao, magnifiquement synchronisé.
Ensuite, Stephan Janssen nous a présenté un reportage sur l’événement Devoxx4Kids mis en place le BeJUG. Devoxx4Kids a eu lieu le 13 et le 20 Octobre dernier et a permis à des adolescents entre 10 et 14 ans de découvrir des Lego Mindstorm, des simulations d’une mission d’exploration sur Mars, NAO, Scratch. Cette initiative a pour objectif de sensibiliser les enfants au monde du développement informatique.Vous pouvez consulter les photos de l’événement.
Cette première édition était couronnée de succès et sera rééditée à la deuxième édition de Devoxx France 2013 qui se tiendra du 27 au 29 Mars 2013.
Nandini Ramani, la vise-présente du pôle développement chez Oracle a présenté les nouveautés de JDK8, suivi de Jasper Potts, un architecte chez Oracle qui nous a fait découvrir une tablette présente dans les locaux des conférences, mise en place par un simple microprocesseur, un écran tactile et du Java.
Après une entrée fanfaronne sur scène en moto, Stephen Chin, l’ambassadeur de Java chez Oracle, a présenté son NightHacking tour qu’il a réalisé en Europe.
La keynote se terminait par une présentation de Neal Ford intitulé “When Geeks Leaks” ou il a expliqué les problématiques liés à une architecture logicielle ou à une méthodologie de développement.
Securing Client side
J’ai suivi la présentation de Mike West, développeur de la l’équipe Google Chrome de Munich, qui a parlé de la sécurité des applications web côté client assuré par HTML5.
Mike West a insisté sur la première règle de
sécurité qui est l’utilisation obligatoire de HTTPS. Avec la nouvelle entête HTTP, il est possible de faire des redirections automatique vers HTTTPS grâce à l’attribut strict-transport-security .
Il a expliqué le mécanisme de l’attaque cross scripting ou XSS : il s’agit de la possibilité d’injection de code (html, javascript, css…) dans la page html afin d’avoir accès aux diverses informations côté client tel que les cookies par exemple.
Il a ensuite présenté la solution de sécurité basé sur ContentSecurityPolicy qui en cours de validation pour devenir un standrad HTML5. Il s’agir de la spécification des seules sources qui seront utilisées par l’application web:
Lire la suite »
Si vous êtes habitués à utiliser maven et eclipse, vous avez peut-être remarqué qu’un agencement standard de projets multi-module provoque un multiple référencement des resources dans les projets eclipse.

Ainsi, lorsque vous faites des recherches dans le workspace, vous obtenez trop d’occurences pour une même resource. De plus, la recherche est d’autant plus longue que les resources sont indéxées de multiples fois.

Reproduire le problème
Pour reproduire le problème,
Lire la suite »
Récemment j’ai refait un peu de JPA/Hibernate et j’ai eu besoin de mapper une relation many-to-many qui nécessite de stocker une information supplémentaire. Pour être concret, j’ai un objet Recette, un objet Ingredient et je souhaite associer une recette à un ingrédient. Ce qui est particulier dans mon cas c’est que j’ai envie de d’enregistrer en plus la quantité de cet ingrédient qui est utilisé dans ma recette de cuisine.
Rien de bien extraordinaire jusque là et je me souvenais avoir déjà géré ce genre de cas. Oui mais voilà, après quelques recherches sur les forums je n’ai trouvé aucune solution clé en main qui ne nécessite pas de parcourir les dizaines de commentaires et de les tester un à un.
Voici la solution que j’ai retenue, je suis bien sur ouvert à toute proposition permettant d’améliorer mon code ! Je ne présenterai ici que les mappings, je vous propose de retrouver l’intégralité des cas d’utilisations sur mon github (avec les Tests Unitaires et surtout le debug hibernate pour voir les requêtes qui passent).
L’idée générale est de découper le many to many en deux relations many-to-one/one-to-many avec l’utilsation d’un objet d’association qui va porter l’information que l’on souhaite rajouter (ici la quantité).
NB: je me sers de lombok pour générer les getter, setter et equals/hashcode de mes classes.
La classe Recipe:
package fr.valtech.many2many.domain;
import java.util.HashSet;
import java.util.Set;
import javax.persistence.CascadeType;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.FetchType;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.OneToMany;
import javax.persistence.Table;
import lombok.EqualsAndHashCode;
import lombok.Getter;
import lombok.Setter;
@Entity
@Table(name = "recipe")
@EqualsAndHashCode(of = { "id", "title" }, callSuper = false)
public class Recipe extends AbstractEntity {
@Id
@Column(name = "recipe_id")
@GeneratedValue(strategy = GenerationType.AUTO)
@Getter
@Setter
private Integer id;
@Getter
@Setter
private String title;
@Getter
@Setter
@OneToMany(fetch = FetchType.EAGER, mappedBy = "pk.recipe", cascade = CascadeType.ALL, orphanRemoval = true)
private Set ingredients = new HashSet();
public void addIngredient(RecipeIngredient i) {
i.setRecipe(this);
ingredients.add(i);
}
} |
La classe Ingredient:
package fr.valtech.many2many.domain;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import lombok.EqualsAndHashCode;
import lombok.Getter;
import lombok.Setter;
@Entity
@EqualsAndHashCode(of = { "id", "label" }, callSuper = false)
public class Ingredient extends AbstractEntity {
@Id
@Column(name = "ingredient_id")
@GeneratedValue(strategy = GenerationType.AUTO)
@Getter
@Setter
private Integer id;
@Getter
@Setter
private String label;
} |
La classe RecipeIngredient qui fait l’association:
package fr.valtech.many2many.domain;
import java.io.Serializable;
import javax.persistence.AssociationOverride;
import javax.persistence.AssociationOverrides;
import javax.persistence.Column;
import javax.persistence.EmbeddedId;
import javax.persistence.Entity;
import javax.persistence.JoinColumn;
import javax.persistence.Table;
import javax.persistence.Transient;
import lombok.EqualsAndHashCode;
import lombok.Getter;
import lombok.Setter;
@Entity
@Table(name = "recipe_ingredient")
@AssociationOverrides({
@AssociationOverride(name = "pk.recipe", joinColumns = @JoinColumn(name = "recipe_id", insertable = false, updatable = false)),
@AssociationOverride(name = "pk.ingredient", joinColumns = @JoinColumn(name = "ingredient_id", insertable = false, updatable = false)) })
@EqualsAndHashCode(of = { "pk", "amount" }, callSuper = false)
public class RecipeIngredient implements Serializable {
@Getter
@Setter
@Column(nullable = false)
private String amount;
@Getter
@Setter
@EmbeddedId
private RecipeIngredientId pk = new RecipeIngredientId();
@Transient
public Recipe getRecipe() {
return getPk().getRecipe();
}
public void setRecipe(Recipe recipe) {
getPk().setRecipe(recipe);
}
@Transient
public Ingredient getIngredient() {
return getPk().getIngredient();
}
public void setIngredient(Ingredient ingredient) {
getPk().setIngredient(ingredient);
}
public RecipeIngredient() {
}
public RecipeIngredient(Ingredient ingredient, String amount) {
setIngredient(ingredient);
this.amount = amount;
}
} |
Et enfin la composite primary key de la classe d’association:
package fr.valtech.many2many.domain;
import java.io.Serializable;
import javax.persistence.CascadeType;
import javax.persistence.Embeddable;
import javax.persistence.FetchType;
import javax.persistence.ManyToOne;
import lombok.EqualsAndHashCode;
import lombok.Getter;
import lombok.Setter;
@Embeddable
@EqualsAndHashCode(of = { "recipe", "ingredient" }, callSuper = false)
public class RecipeIngredientId implements Serializable {
@Getter
@Setter
@ManyToOne(fetch = FetchType.LAZY, cascade = CascadeType.ALL)
private Recipe recipe;
@Getter
@Setter
@ManyToOne(fetch = FetchType.LAZY, cascade = CascadeType.ALL)
private Ingredient ingredient;
} |
Les “astuces” que j’ai mis un moment à comprendre sont:
- dans la classe RecipeIngredientId il faut absolument mettre les relations en LAZY pour éviter un stackOverFlow;
- dans la classe Recipe, la relation OneToMany doit absolument comporter le orphanRemoval = true, sinon la mise à jour et la suppression des RecipeIngredient se passent mal !
Bon tout n’est pas parfait et il faut gérer quelques trucs “à la main”.
Il faut nécessairement persister en base explicitement l’ingrédient que l’on veut utiliser dans une recette que l’on souhaite elle-même persister.
/**
* Parcourt l'ensemble des ingrédient pour voir si ils existe déjà en base ou non.
* @param recipe
*/
private void reatachIngredients(Recipe recipe) {
for (Iterator it = recipe.getRecipeIngredients()
.iterator(); it.hasNext();) {
RecipeIngredient ri = it.next();
Ingredient ingredient = ri.getIngredient();
if (ingredient.getId() != null && ingredient.getId() != 0) {
Ingredient reference = getEntityManager().getReference(
Ingredient.class, ingredient.getId());
ri.setIngredient(reference);
} else {
getEntityManager().persist(ingredient);
}
}
} |
Il faut écrire une requête JPQL pour récupérer la Recette dans son intégralité, si on veut par exemple la serialiser après (certaines relations avaient été mises en LAZY, notamment sur la PK).
Query q = getEntityManager().createQuery(
"select r from Recipe as r "
+ "left join fetch r.recipeIngredients as ri "
+ "left join fetch ri.pk as pk "
+ "left join fetch pk.ingredient "
+ "where r.id = :recipeId");
q.setParameter("recipeId", recipeId);
Recipe recipe = null;
try {
recipe = (Recipe) q.getSingleResult();
} catch (NoResultException nre) {
getLogger().info("no result found");
return null;
} |
Le code complet ici: https://github.com/jbcazaux/many2many

Le 30 Mai, Duchess France fêtera ses 2 ans avec une soirée Trivial Java pour finir une année bien remplie. Au programme des jeux, des cadeaux, des connaissances techniques et beaucoup de bonne humeur.
La soirée aura lieu à l’ECE dans le 15ième. Vous pouvez vous inscrire via le nouveau site de Duchess France http://www.duchess-france.org/2eme-anniversaire-de-duchess-france/ .
Après avoir offert nos locaux l’année dernière, nous avons une fois de plus l’honneur de sponsoriser cet événement sympathique en offrant un modeste buffet.
Venez nous battre au questionnaire java, si vous l’osez

Dans quelques jours aura lieu la première édition de Devoxx France, la déclinaison francophone d’une des plus grande conférence pour les développeurs en Europe.
Elle est organisée par des développeurs indépendants et se déroulera à Paris du 18 au 20 Avril.
Plusieurs thèmes y seront évoqués
- Web, Mobile et le Cloud,
- Langages alternatifs sur la JVM
- Entreprise et pratiques
- Java, Java EE et Architecture
Le groupe Valtech, partenaire de cet événement, sera présent lors des 3 jours. Les consultants Valtech seront eux aussi présents et certains animeront même des présentations. Vous aurez l’occasion de connaître Incanter, une librairie de statistiques écrite en Clojure qui sera présenté par Claude Falguière ou de replonger dans le monde de la recherche d’information avec Majirus Fansi qui vous fera découvrir l’univers d’Apache Lucene et bien d’autres nouveautés.
Pourquoi l’équipe Valtech sera présente ?
- “Découvrir des nouveautés techniques“, Grégory, Développeur, Valtech
- “Participer à des ateliers et tester directement sur ma machine“, Frédéric, Développeur, Valtech
- “Échanger avec d’autres développeurs et partager mes connaissances“, Claude, Consultante senior, Valtech
- “Assister à Devoxx sans avoir à voyager dans un autre pays, c’est à ne pas rater“, Etienne, Scrum Master, Valtech.
Vous souhaitez partager, échanger et vous amuser…
Venez rencontrer l’équipe sur le stand Valtech !
Ne peut-on pas se passer de nos gros frameworks ?
David Gageot est venu fin janvier nous parler de son attitude face aux frameworks en prenant le prétexte de développer un petit site web pour élire le chaton le plus mignon. Ceux qui ont vu le film The Social Network n’ont pas pu manquer la référence à facemash, la première application qu’a développé Mark Zuckerberg pour comparer les étudiantes de son campus.
La présentation donnée au Breizh JUG a été enregistrée en vidéo.
Depuis, David a fait des progrès avec IntelliJ
et a pu nous montrer la maîtrise de son IDE.
Quelle est la différence entre un framework et un outil ?
Un framework va faire bien ce pour quoi il est conçu et difficilement tous les besoins pour lesquels il n’a pas été conçu. L’outil, lui, aide le développeur sans lui poser de contraintes.
Promouvoir la simplicité
Ne pas utiliser de framework, c’est se donner l’occasion de ré-écrire une partie de leur mécanique. On peut donc apprendre comment ils sont fait. On garde aussi la maîtrise intégrale sans ajouter de couche “magique” qui sera à coup sur le talon de l’application en production.
Ce discours est un peu à contre-courant des besoins d’uniformité que les DSI expriment. Nombre d’architectes java seraient inquiets par cette incitation au libre choix des frameworks et des outils. L’équilibre se situe lorsque on met en question le bénéfice de l’uniformisation et les pertes dues à son adoption systématique, comme la complexité par exemple.
Gros framework vs bon outils ?
Voici un résumé des comparaisons qui émergent :
| Fonction |
Gros framework |
Bon outil |
Bénéfice |
| Méthode de développement |
Big Up-Front Design |
TDD |
Feedback |
| Lancement des tests |
N’importe qu’elle usine logicielle |
Infinitest |
Feedback |
| Serveur web |
Tomcat, Play! |
Simple |
Simple |
| Tests web |
|
JWebUnit |
TDD avec des pages web |
| Boîte à outils générique |
Apache Commons |
Guava |
Moderne |
| Moteur de template |
FreeMarker, Velocity |
String Template |
Simple |
| Injection de dépendance |
Spring, Nexus, Pico Container |
Guice |
Simple |
Dans la vidéo, David parle du problème de l’injection de l’aléatoire dans l’application. Il nous a fait la démonstration chez Valtech en utilisant mockito pour injecter la suite de valeurs aléatoires dans le test sans changer le code de production : le code sur la branche Valtech
Nous remercions David d’être passé nous voir, nous avons passé une excellente soirée.
Comme je le disais récemment dans un tweet, j’ai finalement réussi à configurer le build Maven pour que la couverture du code par les tests, affichée dans le dashboard Sonar, tienne compte des tests Fitnesse.

En voila un sujet pointu ! Pourtant, à la suite de ce tweet, plusieurs personnes m’ont demandé une version longue. Je vais donc essayer dans ce message de décrire en détail la configuration complète que nous avons mise en place. J’espère que vous saurez comprendre le fonctionnement de cette solution, pour ensuite l’adapter à votre situation personnelle. Pour ceux qui ne comprennent pas les 3 premiers mot du titre, vous pouvez si vous le désirez vous plonger dans une bonne demi-heure de perplexité et lire la suite de ce message.
Intégration de Fitnesse dans le build Maven
Nous utilisons Fitnesse comme base de documentation pour notre projet. Le wiki contient toute sorte de documentation, technique ou non, concernant le projet ainsi que, bien entendu, tous les spécifications fonctionnelles automatisées.
Remarque : dans la suite de cet article, je parle indifféremment de spécifications fonctionnelles automatisées, de spécifications exécutables ou de tests Fitnesse. Toutes ces appellations désigne la même chose.
Fitnesse n’utilise pas de base de données, mais une hiérarchie de répertoires et de fichiers textes qui représentent les pages du wiki. L’intégralité du projet Fitnesse est contenu dans un répertoire qui est installé dans la gestion de configuration (sur notre projet, nous avons utilisé Git). Cette solution a l’avantage de permettre à tous les développeurs de l’équipe d’avoir une version complète du site en local sur leur poste. Ils peuvent ainsi exécuter les spécifications localement sur leur poste, avant de soumettre le code et les pages Fitnesse correspondantes à l’intégration continue.
Au niveau du build Maven, nous avons dédié un module à Fitnesse. Voici un extrait du répertoire racine de notre projet :

Vous voyez que dans notre répertoire lcwp-fitnesse (lcwp est le nom du projet) se trouve un fichier pom.xml. Le répertoire src ne contient pas de code. Toutes les pages du wiki sont dans le répertoire FitNesseRoot. Vous voyez aussi le fichier fitnesse.jar qui est l’exécutable proprement dit. Bien qu’il ne soit pas recommandé de mettre des binaires en gestion de configuration, nous faisons une exception pour celui-ci car c’est vraiment plus pratique comme ça.
Le plug-in Maven pour auto-générer le classpath utilisé par Fitnesse
Il s’agit du plugin maven-fitnesse-cpgen-plugin qui se trouve être un plugin développé par Valtech. Pour la documentation, suivez le lien suivant : http://source.valtech.com/maven-sites/maven-fitnesse-cpgen-plugin/usage.html
Installation du plugin
Pour utiliser ce plugin il suffit de référencer le dépôt Maven de Valtech (voir le lien ci-dessus) dans votre fichier setting.xml et d’ajouter la section XML suivante dans le fichier lcwp-fitnesse/pom.xml
<plugin>
<groupId>com.valtech.source.maven</groupId>
<artifactId>maven-fitnesse-cpgen-plugin</artifactId>
<version>1.0</version>
<configuration>
<fitnesseRoot>${basedir}/FitNesseRoot</fitnesseRoot>
</configuration>
<executions>
<execution>
<phase>package</phase>
<goals>
<goal>gencp</goal>
</goals>
</execution>
</executions>
</plugin> |
Contenu de la page “root”
Vous devez ensuite modifier le fichier lcwp-fitnesse/FitNesseRoot/content.txt pour qu’il contienne exactement le texte suivant. Ce fichier correspond à la page “root” du site (http://localhost:9090/root) :
!define TEST_SYSTEM {slim}
!include ProjectPath
# begin of maven classpath
# end of maven classpath |
La première ligne ne concerne pas notre sujet. Elle indique que tous nos pages Fitnesse seront exécutés avec le moteur SLIM.
Le plug-in ajoutera tout le classpath maven, dont les dépendances sont décrites dans le pom.xml du module lcwp-fitnesse, entre les deux dernières lignes du fichier.
La page ProjectPath contient des chemins spécifiques qui ne seront pas ajoutés par le plugin et que nous devons donc ajouter nous même. J’en reparlerai plus tard. Elle contient une partie de l’astuce ultime de notre solution.
Problème : ce fichier lcwp-fitnesse/FitNesseRoot/content.txt sera modifié à chaque build et le classpath inséré par Maven sera potentiellement différent pour chaque poste de développeur. Ce fichier ne peut donc pas être géré dans Git (il faut l’ajouter au fichier .gitIgnore).
Astuce : Pour que chaque développeur ne soit pas obligé de créer ce fichier sur son poste, nous avons ajouté la configuration suivante dans notre pom.xml :
<plugin>
<artifactId>maven-antrun-plugin</artifactId>
<version>1.6</version>
<executions>
<execution>
<phase>generate-sources</phase>
<goals>
<goal>run</goal>
</goals>
<configuration>
<tasks>
<echo>Fitnesse root</echo>
<copy file="src/test/resources/fitnesseRootContent.txt"
tofile="${project.basedir}/FitNesseRoot/content.txt" overwrite="true" />
</tasks>
</configuration>
</execution>
</executions>
</plugin> |
Le contenu standard du fichier lcwp-fitnesse/FitNesseRoot/content.txt est enregistré dans le fichier src/test/resources/fitnesseRootContent.txt qui lui est géré dans Git. Ce fichier est recopié au moment du build, juste avant l’éxécution du plugin maven-fitnesse-cpgen-plugin.
Propriétés de la page “root”
Enfin, vous devez changer une propriété de la page root pour quelle soit de type “Suite”. C’est nécessaire pour que le plugin maven trouve la page. Vous pouvez le faire par le wiki en vous rendant à l’URL http://localhost:9090/root, en cliquant sur le bouton “Properties” et en sélectionnant “Suite” pour la propriété “Page type”. Alternativement, vous pouvez aussi directement modifier le fichier lcwp-fitnesse/FitNesseRoot/properties.xml pour qu’il contienne la balise <Suite/>. Voici par exemple le contenu de notre fichier :
<?xml version="1.0"?>
<properties>
<Edit/>
<Files/>
<Help></Help>
<Properties/>
<RecentChanges/>
<Refactor/>
<Search/>
<Suite/>
<Suites></Suites>
<Versions/>
<WhereUsed/>
</properties> |
Le contenu de la page ProjectPath
Le plugin décrit précédemment nous a permis de gérer simplement le gigantesque classpath des dépendances vers les composants externes de notre projet. C’est la page ProjectPath qui contient les chemins vers le code proprement dit de notre projet. Voici son contenu dans notre cas :
!path ../lcwp-web/target/generated-classes/cobertura
!path ../lcwp-web/target/classes
!path ../lcwp-core/target/classes
!path ../lcwp-integration/target/classes
!path ../lcwp-web/target/test-classes
!path ../lcwp-test-support/target/classes |
Le chemin est indiqué relativement au répertoire lcwp-fitnesse, c’est pourquoi tout les chemins sont précédés de “../“.
Et voilà la solution du problème : Pour que la couverture des tests affichée dans le dashboard Sonar tienne compte des tests Fitnesse, il faut que ces derniers soit exécutés sur le code instrumenté par cobertura. Le code instrumenté est généré dans le répertoire target/generated-classes/cobertura. Avec la première ligne de notre fichier nous obtiendrons donc la couverture du code du module lcwp-web.
En développement, le répertoire generated-classes/cobertura est vide et c’est la ligne suivante qui permettra de trouver le code du module lcwp-web.
Pour information, nous avons mis le code des fixtures Fitnesse dans l’arborescence de tests du module lcw-web. C’est donc la ligne ../lcwp-web/target/test-classes qui permet de le trouver.
L’exécution des spécifications par Maven
Tout ce que je viens d’expliquer nous permet d’exécuter les spécifications depuis le wiki.
En local sur le poste d’un développeur, vous devez démarrez Fitnesse avec les commandes suivantes :
cd lcwp-fitnesse
java -jar fitnesse.jar -p 9090 -e 0 |
Ensuite, vous pouvez vous rendre sur le site à l’URL : http://localhost:9090/FrontPage, naviguer jusqu’à la page de spécification qui vous intéresse et cliquer sur le bouton [Test] (dans la colonne de gauche) pour l’exécuter.
Mais ce que nous voulons, c’est que toutes les spécifications Fitnesse soit exécutées lors du build maven, c’est à dire à l’exécution de la commande mvn clean package
Fitnesse vous propose une ligne de commande pour exécuter tous ses tests :
java -jar fitnesse.jar -c DetailedSpecifications?suite&format=text |
DetailedSpecifications est la page du wiki, racine de toutes nos spécifications. Elle est de type “Suite”.
Nous avons tout d’abord ajouté au build un appel à cette commande en utilisant le plugin ant de maven, mais il nous a paru finalement plus simple de faire un appel système dans un tests unitaire jUnit. En effet, la seule façon de savoir si les tests se sont exécutés correctement est d’analyser la sortie standard, le code de retour de cette commande (code de retour de la JVM en fait) étant zéro dans tous les cas. Il est nettement plus facile d’analyser cette sortie en java qu’avec la configuration XML du plug-in ant de maven.
Ce test unitaire doit faire parti des tests du module lcwp-web (c’est la couverture sur ce module qui nous intéresse). Il doit donc se trouver dans l’arborescence lcwp-web/src/test/java/...
J’ai posté le code sur GitHub à l’URL suivante :
https://github.com/etienneCharignon/FitnesseTools/blob/master/fitnesse/tools/FitnesseTest.java
Conclusion
Une belle image vaut mieux qu’un long discours. Nous sommes passés d’un peu plus de 25% à 65,20 % :
