Java EE - EPSI POE mars 2017 - David Gayerie Licence Creative Commons

Tests automatisés

  1. JUnit
  2. Exercice : Tests unitaires de java.util.Math#abs(int)
  3. Utilisation de doublure
  4. Implémentation d'objet mock avec Mockito

Un test automatisé est un programme qui se découpe en trois étapes dites AAA pour Arrange, Act, Assert.

Arrange
La mise en place de l'environnement : création et initialisation des objets nécessaires à l'exécution du test.
Act
Le test proprement dit.
Assert
La vérification des résultats obtenus par le test.

Le sous-système (l'ensemble des objets) éprouvé par le test est parfois appelé SUT (System Under Test).

On distingue différentes catégories de tests :

JUnit

Junit est le framework de tests unitaires le plus utilisé en Java.

Intégration de JUnit dans un projet Maven

Pour intégrer JUnit dans un projet Maven, il faut d'abord ajouter le jar de JUnit dans le projet. Pour cela, il suffit d'ajouter la déclaration suivante dans le fichier pom.xml du projet dans la section <dependencies> :


<dependency>
  <groupId>junit</groupId>
  <artifactId>junit</artifactId>
  <version>4.12</version>
  <scope>test</scope>
</dependency>

Les classes de test peuvent maintenant être implémentées en les plaçant dans le répertoire de source src/test/java du projet.

Structure d'une classe de test JUnit

Comme Java est un langage orienté Objet, les tests JUnit sont regroupés dans des classes de test. Généralement, on groupe dans une classe les tests ayant la même classe comme point d'entrée et on nomme la classe de test à partir du nom de la classe testée préfixé ou suffixé par Test en la plaçant dans le même package. Par exemple, pour tester la classe DateFormatter, on créera une classe TestDateFormatter ou DateFormatterTest.

Exemple d'une classe de test

import org.junit.Test;
import static org.junit.Assert.*;

public class UneClasseTest {
	
  @Test
  public void methodeDeTest() throws Exception {
    assertTrue("Une assertion", true);
  }

}
				

Une classe de test est simplement une classe déclarant des méthodes publiques sans paramètre annotées par @Test.

Il est possible d'exécuter des méthodes avant et après chaque test pour allouer et désallouer des ressources nécessaires à l'exécution des tests. On utilise pour cela des méthodes publiques sans paramètre annotées avec @Before et @After.

Exemple d'une classe de test avec @Before et @After

import org.junit.After;
import org.junit.Before;
import org.junit.Test;

import static org.junit.Assert.*;

public class UneClasseTest {
	
  @Before
  public void initTestEnvironment() {
    // cette méthode est exécutée avant chaque test
  }

  @After
  public void destroyTestEnvironment() {
    // cette méthode est exécutée après chaque test
  }

  @Test
  public void methodeDeTest() throws Exception {
    assertTrue("Une assertion", true);
  }

}
				

La classe Assert est une classe outil contenant des méthodes statiques permettant de déclarer les assertions des tests. Les assertions sont des méthodes suivants les mêmes patrons de signature :


  // les assertions impliquant uniquement la valeur récupérée par le test
  // comme (Assert.assertTrue)
  Assert.assertXXX(message, valeurActuelle);
  Assert.assertXXX(valeurActuelle);
  
  // les assertions impliquant une comparaison entre une valeur attendue 
  // et la valeur récupérée par le test (comme Assert.assertEquals)
  Assert.assertXXX(message, valeurAttendue, valeurActuelle);
  Assert.assertXXX(valeurAttendue, valeurActuelle);

Exécution des tests JUnit

JUnit est pris en charge par les IDE Java et par les outils de build comme Maven.

Dans Eclipse, il suffit de faire un clique droit dans l'explorateur de projet sur un fichier source, une classe de Test ou un package et de choisir "Run as... > JUnit Test". On peut également presser la touche F11 dans l'éditeur de code source de la classe de test.

Avec Maven, il suffit de lancer le goal test :

mvn test

Exercice : Tests unitaires de java.util.Math#abs(int)

Objectif
Écrire les tests unitaires pour la méthode java.util.Math#abs(int).
Modèle Maven du projet à télécharger
tests_unitaires.zip
Mise en place du projet
Éditer le fichier pom.xml et modifier la section <developers> pour indiquer vos nom et email.
Intégration du projet dans Eclipse
L'intégration du projet dans Eclipse suit la même procédure que celle vue lors de l'introduction à Maven

L'exercice précédent proposait de tester une méthode sans effet de bord (ce que l'on appelle également une fonction pure). Les tests sur ce type de méthodes sont faciles à écrire. Ils restent cependant l'exception lorsqu'on utilise la programmation orientée objet. En effet, l'appel d'une méthode sur un objet modifie le plus souvent son état et provoque généralement des effets de bord en sollicitant d'autres objets avec lesquels l'objet entretient des dépendances.

Utilisation de doublure

Parfois, il est utile de contrôler l'environnement de test d'un objet ou d'une collaboration d'objets. Pour cela, on peut faire appel à des doublures qui vont se substituer lors des tests aux objets réellement utilisés lors de l'exécution de l'application dans un environnement de production.

Simulateur
Un simulateur fournit une implémentation alternative d'un sous-sytème. Un simulateur remplace un sous-sytème qui n'est pas disponible pour l'environnement de test. Par exemple, on peut remplacer un système de base de données par une implémentation simplifiée en mémoire.
Fake object
Un fake object permet de remplacer un sous-sytème dont il est difficile de garantir le comportement. Le comportement du fake object est défini par le test et est donc déterministe. Par exemple, si un objet dépend des informations retournées par un service Web, il est souhaitable de remplacer pour les tests l'implémentation du client par une implémentation qui retournera une réponse déterminée par le test lui-même.
Mock object
Un objet mock est proche d'un fake object sauf qu'un objet mock est également capable de faire des assertions sur les méthodes qui sont appelées et les paramètres qui sont transmis à ces méthodes.

Implémentation d'objet mock avec Mockito

Mockito est un framework Java pour faciliter la création d'objets mocks à partir d'une classe ou d'une interface.

Pour ajouter Mockito à un projet Maven, il suffit d'ajouter dans la section <dependencies> du fichier pom.xml :


<dependency>
  <groupId>org.mockito</groupId>
  <artifactId>mockito-all</artifactId>
  <version>1.9.5</version>
  <scope>test</scope>
</dependency>

La méthode statique Mockito.mock(Class<?>) permet de créer une instance d'un mock à partir d'une classe ou d'une interface. L'instance d'objet retournée par cette méthode est instrumentalisée par Mockito. Il est possible d'enregistrer sur ce mock des comportements lors de la phase arrange grâce, notamment, à la méthode Mockito.when(Object). Lors de la phase assert, il est possible de vérifier que les appels de méthodes programmés sur le mock ont bien été réalisés grâce à la méthode Mockito.verify(Object).

Exemple d'une classe de test utilisant Mockito

import static org.hamcrest.CoreMatchers.*;
import static org.junit.Assert.*;

import javax.servlet.http.HttpServletRequest;

import org.junit.Test;
import org.mockito.Mockito;

public class TestWithMockito {

  @Test
  public void testDemoMockito() throws Exception {
    HttpServletRequest mockedRequest = Mockito.mock(HttpServletRequest.class);
    Mockito.when(mockedRequest.getParameter("login")).thenReturn("monlogin");

    String parameterValue = mockedRequest.getParameter("login");

    assertThat(parameterValue, is("monlogin"));
    Mockito.verify(mockedRequest).getParameter("login");
  }

}

Utilisation de Mockito avec import static

import static org.hamcrest.CoreMatchers.*;
import static org.junit.Assert.*;
import static org.mockito.Mockito.*;

import javax.servlet.http.HttpServletRequest;

import org.junit.Test;

public class TestWithMockito {

  @Test
  public void testDemoMockito() throws Exception {
    HttpServletRequest mockedRequest = mock(HttpServletRequest.class);
    when(mockedRequest.getParameter("login")).thenReturn("monlogin");

    String parameterValue = mockedRequest.getParameter("login");

    assertThat(parameterValue, is("monlogin"));
    verify(mockedRequest).getParameter("login");
  }

}

La documentation de Mockito est accessible ici.