Publié par

Il y a 9 ans -

Temps de lecture 10 minutes

NoThunes, tests en tout genre et qualité de code

Avec un peu de retard sur les autres, voici le cinquième article de cette série sur Grails. Nous allons parler ici de :

  • Tests unitaires
  • Tests d’intégration
  • Mesures de qualité de code
  • Intégration continue

En appliquant le tout à notre projet test bien aimé : NoThunes.

Tout code sera testé, sinon par vous, alors par vos utilisateurs …

La nécessité de tester notre code, quel qu’il soit, a été largement décrite, soutenue et argumentée. Par conséquent, nous ne reviendrons pas dessus dans cet article. Je tiens à m’excuser pour avoir placé en dernier lieu l’activité qui devrait ouvrir le bal dans la rédaction d’une application (le TDD c’est bon, mangez-en). Cela m’a paru plus attractif de mettre les mains dans le cambouis de suite. Mais maintenant nous allons passer aux choses sérieuses : les Tests.

Scott Davis, sur le blog d’IBM, a rédigé une série d’articles Mastering Grails dont 2 nous concernent plus particulièrement ici :

Les tests à la mode Grails sont largement bien décrits dans ces 2 articles. Je vais donc vous fournir ici mon retour d’expérience sur l’application en conditions réelles de ces conseils. Les tests unitaires au sens propre sont très bien décrits dans ces 2 articles, nous allons donc directement sauter aux tests d’intégration.

Tests d’intégration Grails dans la pratique

Durant la phase des tests d’intégration, l’application est chargée entièrement, dans un mode test. C’est l’équivalent du lancement de grails test run-app. Par conséquent nous disposons d’une base de données et de tous nos composants.

D’après Burt Beckwith, membre de la team Grails, il ne faut pas utiliser le framework de mock de Grails pour tester les classes de domaine. Sinon, tout ce qui est testé, c’est le framework de mock en lui-même et il ne supporte pas toutes les fonctionnalités de GORM (comme les closures withCriteria par exemple !). Pour les classes de domaine, il faut donc utiliser uniquement des tests d’intégration. L’exception à cette règle concerne les tests de méthode n’impliquant ni GORM, ni la base de données, comme les tests de validation de contraintes (regarder du côté de mockForConstraintsTests() pour ça).

Cette méthode est un peu radicale et suppose que, dès qu’on fait usage, à un quelconque endroit, de requêtes nommées ou de closures Criteria, on se retrouve obligé de ne coder que des tests d’intégration. Une alternative consiste à utiliser GMock pour combler les manques actuels de Grails.

Jeux de données

Lorsqu’on code des tests d’intégration il est important de connaitre le contenu de la base de données avant chaque test. Il est donc utile de pouvoir se baser sur des jeux de données pour initialiser la base. Parmi les nombreuses solutions qui s’offrent à nous, voici celles qui ont retenu mon attention :

  • Utiliser la classe de configuration Bootstrap.groovy : Cela suppose de charger l’intégralité des données utiles pour tous les tests d’intégration, ce qui peut vite devenir encombrant. De plus le code nécessaire à instancier, lier et sauver toutes les instances utiles est assez verbeux.
  • Le plugin DbUnit Operator : Permet d’injecter à la demande des jeux de données écrits en XML. Ce plugin est basé sur la librairie du même nom. C’est robuste et souple à la fois. L’inconvénient est que le modèle XML est centré sur le schéma de la base et va nécessiter une maintenance non négligeable.
  • Le couple de plugin Fixtures/BuildTestData : Fixtures permet de créer des jeux de données et de les sauvegarder en base rapidement avec une syntaxe concise. C’est la solution qui m’a semblée la plus pratique. L’association avec le plugin BuildTestData autorise à raccourcir encore la définition d’entités. Les champs non remplis manuellement dans une fixture peuvent être remplis automatiquement par BuildTestData.

Après expérimentation, la dernière solution s’est révélée la plus simple et efficace. La documentation du plugin est un peu succincte et ne décrit pas de façon explicite tous les cas d’utilisation. En exclusivité, je vais prendre comme exemple un cas non documenté, sous vos yeux ébahis : l’utilisation conjointe des 2 plugins dans une fixture externe. Commençons par l’installation :

grails install-plugin fixtures
grails install-plugin build-test-data

L’installation du plugin Fixtures entraine l’apparition d’un répertoire fixtures/ à la racine du projet. Tous les fichiers de fixtures déposés dedans seront rendus accessible pour être chargé dans le code de nos tests. Il est possible de mettre directement les fixtures dans notre code (comme décrit dans la documentation) mais j’ai préféré l’option de chargement de fichiers externes pour éviter d’introduire du bruit dans le code. Voici une fixture appliquée à notre modèle :

fixtures/initialDev.groovy

import fr.xebia.nothunes.domain.*
import fr.xebia.nothunes.security.*

def member = User.findByUsername('user')
member.confirmPasswd = member.passwd

build {
  rtnp(Band) {
    name = 'RTNP'
    webSite = 'http://rtnp.org'
    owner = member
  }
  promotionCanape(Album) {
    name = "Promotion Canapé"
    band = rtnp
  }
  zombieNow(Track) {
    name = "Zombie Now"
    album = promotionCanape
  }
}

Décryptons ce fichier. Nous commençons par récupérer une instance de compte utilisateur en base, pour lui attribuer un Band. Cet utilisateur a été inséré dans la base via la classe de configuration Bootstrap, on peut donc compter dessus. La closures build sera interprétée par Fixtures pour créer les objets et les sauver en base. Fixture permet d’utiliser une closure build, qui s’appuie sur le plugin BuildTestsData, qui remplira automatiquement les champs non definis dans la fixture, de façon à ce que l’instance créée respecte ses contraintes d’intégrité.

Il est important de noter que les liens tissés entre les objets de la fixture sont écrits de façon directe. Par exemple la propriété band de notre instance promotionCanape est directement le nom de l’instance décrite juste au dessus. Pas besoin de sauvegarde intermédiaire, ce qui fait gagner pas mal de temps d’écriture.

Maintenant que nous disposons d’un petit jeu de données, pour le charger dans un test, il suffit de faire comme ceci :

package fr.xebia.nothunes.domain

import grails.test.*
import fr.xebia.nothunes.domain.*
import fr.xebia.nothunes.security.*

class TrackIntegTests extends GrailsUnitTestCase {

  // definition de l'attribut fixtureLoader pour injection automatique
  def fixtureLoader

  protected void setUp() {
    super.setUp()
  }

  protected void tearDown() {
    super.tearDown()
  }

  void testLoadFixture() {

    // chargement du fichier fixtures/initialDev.groovy
    def fixture = fixtureLoader.load('initialDev')

    def result = Track.listOrderByName()

    assertEquals 1, result.size()
    assertEquals 'Zombie Now', result[0].name
  }

  void testNotLoadedFixture() {

    def result = Track.listOrderByName()

    assertEquals 0, result.size()
  }
}

Comme on peut le voir, la durée de vie des données en base ne dépasse pas la méthode de test. Ce comportement est plutôt pratique pour éviter que nos tests d’intégration se polluent les uns les autres.

Test fonctionnels

Pour aller encore un peu plus loin dans les tests, il faut monter encore d’un cran et passer aux tests d’IHM. Une référence dans le monde du web en Java : Selenium. C’est le premier nom qui m’est venu et j’ai donc cherché un plugin Grails. Il existe bel et bien mais la documentation initiale m’a fait faire demi-tour. Les tests peuvent être rédigés en trois formats différents, je vous colle des exemple de chacun pour que vous puissiez juger :

  • HTML :



test1


test1
open/selenium-test/
clickAndWaitlink=BookController
clickAndWaitlink=New Book
typetitlebook1
clickAndWait //input[@value='Create']
clickAndWait link=Book List
verifyTextPresent book1
  • Pipe separated values :
open|/selenium-test
clickAndWait|link=BookController
clickAndWait|link=New Book
type|title|book2
clickAndWait|//input[@value='Create']
clickAndWait|link=Book List
verifyTextPresent|book2
  • Groovy Server Pages :










Si l’une de ces syntaxes vous emballe, faites en bon usage. Personnellement j’ai été refroidi par l’aspect très compact et plutôt dur à relire. J’ai donc continué mes investigations, et suis finalement tombé sur le plugin WebTest, basé sur la librairie de Canoo. Une librairie beaucoup plus orienté Groovy que ne l’est le plugin Selenium. De plus WebTest fourni également de jolis rapports HTML plein de couleurs attrayantes, faciles à interpréter. Voici un exemple de ce que peut donner le code d’un test simpliste avec cette librairie :

package nothunes

import nothunes.common.WebTestsNoThunes

class BandWebTests extends WebTestsNoThunes {

  def fixtureLoader

  void testLogin() {
    invoke(url:'login')
    verifyText(text:'Please Login..')

    def fixture = fixtureLoader.load('initialDev')
    invoke(url:'/navigation')
    verifyText(text:'RTNP')
  }
}

Les actions sont basiques, l’API est nativement en Groovy, et pour ceux qui ont déjà utilisé Selenium sur des projets Java, ce n’est vraiment pas dépaysant. La facilité de prise en main et les rapports parlants en font ma préférence pour l’automatisation de tests d’IHM web.

webtests-report

Analyse statique du code

Outre la pose d’un filet de sécurité autour des fonctionnalités de l’application, il est également intéressant d’affiner la qualité du code source produit. L’analyse statique de code est une méthode rodée chez les développeurs Java pour permettre de trouver des duplications de code, des bugs éventuels, évaluer la complexité, etc. Du coté du langage Groovy, l’outillage est un peu moins riche mais on trouve quand même quelques petites choses intéressantes.

CodeNarc

Tout d’abord : CodeNarc. Ce programme permet de valider du code Groovy en regard de règles de programmation établie. Un jeu de règle est proposé par défaut mais il est possible de définir ses propres règles. Un packaging sous forme de plugin Grails existe également, pour permettre une installation et une utilisation simplifiées :

grails install-plugin codenarc
grails codenarc

Cela va lancer l’analyse et générer un rapport détaillé de toutes les violations de règles, au format HTML :

Extrait de CodeNarcReport.html

codenarc_nothunes

La présentation est spartiate, mais ça tourne ! Le rapport commence par une vision macro avec la consolidation des erreurs par package, puis viens la liste détaillée de chacun des packages concernés, et enfin la description de chaque type de violation rencontré.
On peut également écrire soit même des règles de programmation à respecter. Il est donc aisé de se baser sur cet outil pour faire respecter les normes de votre choix. Pour ceux d’entre vous qui l’utilise déjà, sachez que la dernière version (la 0.11) est sortie le 13 novembre avec notamment 50 règles supplémentaires.

GMetrics

GMetrics, quant à lui, est centré sur le calcul et la présentation de métriques. Le projet assure le calcul :

  • de complexité cyclomatique
  • du nombre de lignes par méthode
  • du nombre de lignes par classe

Selon le même principe que CodeNarc, l’installation de cette librairie se fait comme un plugin au projet : il se lance simplement et génère un rapport HTML :

grails install-plugin gmetrics
grails gmetrics

Extrait d’un rapport GMetrics

gmetrics_nothunes

GMetrics est encore assez léger dans les métriques collectées, mais il offre une excellente base de développement pour collecter ses propres métriques. Si par hasard vous en écrivez pour un projet, n’hésitez pas à en faire profiter la communauté.

Conclusions

Il y aurait de quoi écrire un livre entier sur le seul sujet des tests dans le cadre de Grails. Comme d’habitude, les affinités entre le langage Groovy et le Java font que beaucoup d’outils Java sont déjà pluginizé ou ne coûterai pas cher à l’être. Longtemps cantonné aux scripting Java, le langage Groovy trouve ses lettre de noblesse avec Grails. Il est agréable de voir se développer pour le langage Groovy des outils de qualité qui apportent tant au langage Java. En quelques recherches, un projet peut se doter de tout ce qu’il faut pour développer des tests unitaires, d’intégration, fonctionnels mais aussi tests d’acceptance (avec Fitnesse ou EasyB), ou même des tests de vos fonctions Javascript avec JsUnit.

Dans le framework Grails, les tests sont disponibles pour tous les goûts, toutes les tailles. Le véritable défi consiste à séparer le bon grain de l’ivraie au milieu du foisonnement de plugins disponibles (sans compter qu’il est toujours possible d’intégrer soit même des librairies Java). J’espère vous avoir apporté, sinon La réponse, au moins l’éclairage de mon expérience personnelle sur le sujet. Maintenant il ne reste plus qu’à trouver votre propre style de tests Grails.

Ressources :

Publié par

Publié par Aurélien Maury

Aurélien est passionné par les frameworks web haute productivité comme Grails, JRuby on Rails ou Play! framework. Il est également intéressé par tous les aspects de performance et d'optimisation (mais aussi par la phytothérapie, la PNL, la basse électrique, la philosophie et pleins d'autres sujets). Il est également formateur au sein de Xebia Training .

Commentaire

2 réponses pour " NoThunes, tests en tout genre et qualité de code "

  1. Publié par , Il y a 5 ans

    Bonjour Monsieur,

    je souhaite mettre en place une formation Fitnesse pour une équipe Web 4G de l’insee.
    Je ne vous cache pas qu’il est est très dificille de trouver un prestataire pour animer cette formation.
    Pouvez vous m’aider à touver un prestataire ?

    Bien cordialement,
    Dominique Penot

Laisser un commentaire

Votre adresse de messagerie ne sera pas publiée. Les champs obligatoires sont indiqués avec *

Nous recrutons

Être un Xebian, c'est faire partie d'un groupe de passionnés ; C'est l'opportunité de travailler et de partager avec des pairs parmi les plus talentueux.