GWT & les tests, épisode 3

A la fin du précédent article, nous en étions restés à une application GWT testée unitairement :

  • tous les comportements des contrôleurs sont testés
  • les points difficiles des vues sont testés

Ces tests sont exécutés avec JUnit ou Maven, comme n’importe quel autre test. Nous sommes donc capables de lancer l’application GWT dans une JVM standard. Rien ne s’affiche, mais toutes les classes sont instanciées, et les comportements sont implémentés. Par contre la partie serveur (qui reçoit les appels GWT-RPC) est mockée.

La faiblesse de ces tests est qu’ils sont unitaires : on teste le comportement d’un écran. Or les problèmes commencent lors que l’on enchaine les écrans…

Des tests d’intégrations

Dans notre cas, la partie serveur est faite avec Spring. Nous la testons donc avec le SpringJUnit4ClassRunner, qui nous démarre tout la partie serveur dans JUnit. En ajoutant un peu de glue, nous avons donc « bouclé la boucle » : au lieu de mocker la partie serveur de l’appli GWT, nous avons branché la partie IHM sur la partie serveur en Spring :

full

L’application GWT et son serveur sont donc complètement démarrés et opérationnels dans une JVM pour faire des tests.

  • lancer la partie serveur
  • lancer l’application GWT (en appelant son EntryPoint, tout simplement)
  • stimuler l’application GWT, qui va appeler la partie serveur
  • enchainer les écrans

Ce ne sont plus des tests unitaires, ce sont de véritables tests d’intégrations.

En pratique

Aucune modification coté serveur n’a été nécessaire. Nous avons juste ajouté un peu de glue pour brancher l’application GWT sur la partie Spring. Avant, nous avions déjà des tests d’intégrations qui mockaient la partie « sous » le serveur : bases de données, WebServices… Nous avons juste réutilisé cet environnement.

Pour stimuler l’application GWT, on peut faire du Java. Par exemple :

1
2
MyView myView = (MyView) RootPanel.get(0);
myView.getButton().click();

Ce n’est pas très pratique, il faut mettre des getters partout. En plus, on va souvent vouloir aller chercher la 4ème checkbox située dans la table X elle-même située dans le conteneur Y lui-même situé dans le RootPanel. C’est pourquoi nous avons développé un petit langage ressemblant à XPath permettant d’aller chercher des composants et d’appeler des méthodes dessus.
Par exemple, pour s’assurer qu’un label situé dans le premier widget d’un conteneur, lui même dans le troisième widget du RootPanel, cela donne :

1
/rootPanel/widget(2)/widget(0)

Son texte, accessible normalement par getText() est accessible par

1
/rootPanel/widget(2)/widget(0)/text

Tout cela en utilisant lourdement l’introspection Java.

Nos scenarii de tests sont donc écrit à base de ces XPath, en CSV. Voici un exemple de scénario CSV qui :

  • Démarre l’application GWT
  • Vérifie le contenu du label est égal à ‘toto’
  • Simule un click sur un bouton, qui appel un service Spring via GWT RPC, et remplace le contenu du label
  • Vérifie que le contenu d’un label a changé et est égal à ‘titi’

Voila le scénario

1
2
3
4
initApp;
assertExact;toto;/rootPanel/widget(2)/widget(0)/text
click;/rootPanel/widget(2)/widget(1)
assertExact;titi;/rootPanel/widget(2)/widget(0)/text

Un peu de code permet de lancer ce scénario dans JUnit. On l’exécute donc de la même façon que les tests unitaires, ils sont juste plus long (à cause du démarrage de la partie serveur).

La partie test d’intégration est aussi dans le projet gwt-test-utils. La documentation est ici.

On aurait pu faire a peu près la même chose avec Sélénium. Il y a deux grosses différences :

  • GWT se prête assez mal à Sélénium à cause des ID des composants (cela peut cependant se faire). Notre langage de localisation, est pour nous, beaucoup plus simple et efficace
  • Nos tests sont lancés par JUnit, donc dans Eclipse, ou dans le build Maven, ce qui rend leur exécution très rapide.

Bilan

Dans le cadre de notre projet, nous avons écrit 40 scenarii en CSV. Certains sont assez long (200 lignes). Ces 40 scenarii nous assurent une non régression totale sur l’ensemble des fonctionnalités de notre application !

  • L’investissement de départ n’a pas été très fort, car ce système a été développé au fur et à mesure
  • Les bénéfices sont énormes : en quasiment un an de développement, nous n’avons eu quasiment aucune régression !
  • La maintenance des scenarii est couteuses (encore que, quelques heures tous les 15 jours), mais faible en face des bénéfices.
  • La non régression complète (en environnement mocké) prends 3 minutes.
  • Nous faisons régulièrement des refactors sur l’application GWT sans même lancer GWT : si les tests de non régressions passent, nous n’avons rien cassé.

GWT s’avère une technologie IHM qui, via quelques outils, permet de faire des tests de manière extrêmement poussé. Ce qui augmente encore la productivité de cette technologie !