Play Framework et les Tests Unitaires

28 Aug 2013



Depuis quelques jours, je joue avec Play!, un framework web Java.
Il possède plusieurs avantages forts sympathiques je trouve:

  • Stateless
  • REST
  • Asynchrone
  • Intgérant nativement Junit
Et c'est de ce dernier point en particulier que je vais parler aujourd'hui car l'intégration n'est pas si native que cela et je suis tombé sur plusieurs bugs gênants.

play test ne fait rien


La méthode conseillée pour lancer les tests est la commande "play test" mais malheureusement avec la version 2.1.3, cette commande ne teste absolument rien. Gênant ce bug...
Pour contourner cela il est nécessaire d'ajouter dans le fichier project/Build.scala les lignes suivantes:


val appDependencies = Seq(
// Add your project dependencies here,
javaCore,
javaJdbc,
javaEbean,
"play" %% "play-test" % play.core.PlayVersion.current % "test" exclude("com.novocode", "junit-interface"),
"com.novocode" % "junit-interface" % "0.9" % "test"
)


Ce problème devrait être corrigée dans la 2.1.4 qui ne devrait pas tarder à sortir.

play test se bloque lors des tests IHM


Il est possible avec Play! Framework de réaliser des tests fonctionnels en testant directement une page de notre application. Pour cela on utilise un testbrowser (cf la documentation sur le sujet).

Malheureusement si l'on fait cela avec la version actuelle de Play! Framework cela va bloquer entièrement la commande "play test" qui ne sera donc plus très utile.

Cela est dû à une incompatibilité avec la version de JQuery livré avec Play! Framework d'après ce bug.
La seule façon de débloquer cela est de télécharger la version 1.8.3 de jquery et de l'ajouter dans le dossier public/javascript en remplacement de la version 1.9.0 (ne pas oublier de mettre à jour vos pages aussi).

Lancer les tests unitaires depuis Eclipse


La commande "play test" est sympathique mais il arrive très régulièrement que l'on souhaite lancer uniquement un test ou une classe de tests depuis notre Eclipse et là malheureusement cela ne marche pas comme attendu à cause de l'erreur suivante:


java.lang.IllegalStateException: Class [class play.db.ebean.Model] is enhanced and [class models.Company] is not - (you can not mix!!)



Pour corriger cela il est nécessaire de télécharger le jar d'ebean et de l'ajouter en tant que paramètre par défaut dans Eclipse. Pour cela il est nécessaire d'aller dans Preferences > Java > Installed JRE > Edit et d'ajouter la ligne suivante:


-javaagent:/path/to/ebean/ebean-2.7.3-agent.jar



Et voilà ça devrait mieux aller !


Vraiment ? Pas dans tous les cas en fait. Il peut arriver que suite à cela un test soit cassé car lors d'un select en base via Ebean qui renvoie un objet avec un champ null.
Dans mon cas il s'agissait d'un champ Text (ou @Lob pour Ebean) qui était systématiquement vide alors que l'objet inséré le contenait bien.

Pour corriger cela il faut passer le champ concerné en private et générer soit même les getter/setter au lieu de laisser Play! le faire tout seul.
Et c'est tant mieux, je n'aime pas le fait d'avoir l'ensemble de mes champs public ça casse un peu le principe de l'encapsulation.

Fichier "init-data.conf" non trouvé


Si vous avez suivi les tutoriels du site Play! Framework vous avez vu que l'on peux ajouter facilement des données aux tests ou à l'application en cours de développement en créant un fichier Yaml dans le dossier conf/ et contenant l'ensemble des éléments à créer en base.

Le problème étant que si l'on fait les tests ensuite via Junit dans Eclipse il ne va jamais trouver ce fichier et donc ne va pas précharger votre base de données...

Pour corriger cela il faut ajouter le dossier conf au classpath d'Eclipse car celui-ci n'est pas ajouté lors de la commande "play eclipse".

Pour l'ajouter il suffit de faire un click droit sur le dossier conf/ dans le projet et choisir "Build Path" > "Use as Source Folder".

Je veux le coverage de mon code !


Maintenant que l'on a des tests qui fonctionnent que ça soit via la commande play test ou dans Eclipse on aimerait bien avoir également la couverture de code que l'on teste. Nous allons utiliser jacoco pour générer notre coverage.

Pour cela il faut ajouter plusieurs éléments à notre configuration:

  • Dans le fichier project/plugins.sbt ajouter les lignes suivantes:
libraryDependencies ++= Seq(
"org.jacoco" % "org.jacoco.core" % "0.5.9.201207300726" artifacts(Artifact("org.jacoco.core", "jar", "jar")),
"org.jacoco" % "org.jacoco.report" % "0.5.9.201207300726" artifacts(Artifact("org.jacoco.report", "jar", "jar")))

addSbtPlugin("de.johoop" % "jacoco4sbt" % "1.2.4")
  • Dans le fichier project/Build.scala:
  • Ajouter sous les import déjà existant les lignes suivantes:
import de.johoop.jacoco4sbt._
import JacocoPlugin._
  • Ajouter sous le bloc "val appDependencies = Seq(...)" une ligne:
lazy val s = Defaults.defaultSettings ++ Seq(jacoco.settings:_*)


  • Puis modifier le dernier bloc pour qu'il ressemble à cela (attention à bien ajouter le paramètre settings = s):
val main = play.Project(appName, appVersion, appDependencies, settings = s).settings(
    
parallelExecution in jacoco.Config := false,    
jacoco.reportFormats in jacoco.Config := Seq(XMLReport("utf-8"), HTMLReport("utf-8")),    
jacoco.excludes in jacoco.Config := Seq("views.*", "controllers.Reverse*", "controllers.javascript.*", "controllers.ref.*", "Routes*")
)



Et voilà la couverture peut maintenant être générée via la commande:

play jacoco:cover



L'ensemble des fichiers seront placés dans le dossier /target/scala-2.9.1/jacoco/html.


Petit exemple du look:





Post 11/52