Tests unitaires avec JUnit3

Version utilisée : JUnit 3.8.1

Introduction

Documents de référence

JUnit fournit une API permettant d'organiser les tests unitaires d'une application.
Les tests unitaires sont regroupés en TestCase, qui contiennent en général les tests unitaires liés à une classe.
Les TestCase sont ensuite regroupés en TestSuite, ce qui permet d'exécuter tous les test cases en une seule commande.

TestCase : tester une classe

Utilise comme exemple les tests unitaires de la classe Pile, question1 du TP3.

Par convention, les tests liés à une classe MyClass sont effecués dans une classe nommée MyClassTest.
Une classe de test doit avoir la forme suivante :
public class PileTest extends junit.framework.TestCase {

    public PileTest() { }

    protected void setUp(){ ... }

    protected void tearDown(){ ... }

    // ici les méthodes contenant les tests unitaires    
}
Par convention, les méthodes de test commencent par test.

Le constructeur, setUp() et tearDown() sont optionnels.

Le constructeur est appelé une seule fois à la création de la classe ; est parfois utile à JUnit (serialization).

setUp() sert à contenir du code d'initialisation.
Les objets créés pour servir aux tests sont appelés fixtures.

tearDown() sert à contenir du code de nettoyage.

JUnit garantit que setUp() est appelée avant l'exécution de chaque méthode de test.
JUnit garantit que tearDown() est appelée après l'exécution de chaque méthode de test.
Ainsi, chaque test peut s'effectuer indépendamment des autres, avec des données fraîchement construites.

Exemple pour PileTest :
public class PileTest extends junit.framework.TestCase {
    
    Pile p1, p2;

    protected void setUp(){
        p1 = new Pile();
        p2 = new Pile(2);
    }

    public void testVideApresConstructeur() {
        assertTrue("La pile p1 devrait être vide", p1.estVide());
        assertTrue("La pile p2 devrait être vide", p2.estVide());
    }
    
    public void testEstPleine() {
        try{
            p2.empiler("chose1");
            p2.empiler("chose2");
            assertTrue("La pile p2 devrait être pleine", p2.estPleine());
        }
        catch(PilePleineException e){
            System.out.println("--- PilePleineException inattendue dans testEstPleine() ---");
        }
    }
    
    // etc.
    
}
Les fixtures sont ici p1 et p2.

Lors de l'exécution de ce test : Dans une méthode de test, on utilise des assertions, méthodes de la classe Assert.
Voir la liste de méthodes disponibles dans le javadoc de Assert
assertTrue() et assertEqual() font partie des méthodes les plus couramment utilisées.

Exécuter un test

Pour compiler une classe de test, ne pas oublier de mettre junit.jar dans le classpath.

Une fois la classe de test compilée, il faut exécuter junit.textui.TestRunner en mettant le nom de la classe en paramètre :
java -classpath /path/to/classes:/path/to/junit.jar junit.textui.TestRunner question1.PileTest

Exécuter une suite de tests : TestSuite

Pour rassembler plusieurs test cases en une seule exécution :
import junit.framework.TestSuite;
import junit.framework.Test;

public class MyTestSuite extends TestSuite{
    public static Test suite() {
        TestSuite suite= new TestSuite();
        suite.addTestSuite(Class1Test.class);
        suite.addTestSuite(Class2Test.class);
        return suite;
    }
}
Pour compiler, junit.jar doit être dans le classpath.

L'exécution de la test suite se fait de la même manière que celle d'un test case :
java -classpath /path/to/classes:/path/to/junit.jar junit.textui.TestRunner MyTestSuite

Tester les exceptions

On peut tester qu'une exception est bien lancée dans un test unitaire en utilisant junit.framework.Assert.fail().
Voir une utilisation dans exemples/java/junit3/exception/ :
    public void test4() {
        Compteur c = new Compteur();
        try{
            c.ajoute(-4);
            Assert.fail();
        }
        catch(CustomException e){
            // test réussi
        }