Test unitaire avec JUnit 4

Transcription

Test unitaire avec JUnit 4
Tests unitaires en TDD
avec JUnit sous Netbeans
I. Introduction
Un test unitaire est un bout de code écrit par un développeur pour tester une
fonctionnalité dans un code. Le test unitaire est utilisé pour s'assurer que la
fonctionnalité écrite fonctionne correctement mais aussi pour s'assurer que cette
fonctionnalité continue de fonctionner après des modifications apportées au code
lors de la reingénierie.
JUnit est un framework de test unitaire pour le langage de programmation
Java. JUnit est intégré à Netbeans.
II. Organisation des fichiers
Les tests unitaires doivent être dans un dossier nommé test. L'organisation de
ce dossier suit la même organisation que le dossier src. Un fichier dans test a
donc le même nom de paquet que la classe qu'il teste, par exemple la classe
fr.ensicaen.ecole.toto.MaClasse.java du dossier src aura comme nom de
fichier fr.ensicaen.ecole.toto.MaClasseTest.java dans le dossier test.
École Nationale Supérieure d'Ingénieurs
& Groupe de Laboratoires, C.N.R.S.
6, Boulevard Maréchal Juin, F-14050 CAEN cedex
http://www.ensicaen.fr
2 ■ Tests unitaires en TDD avec JUnit sous Netbeans
III. JUnit sous Netbeans
III.1 Créer une classe de test
Dans le cas d'un développement en TDD, on commence par créer la classe de
test avant la classe à tester.
La création se fait par le menu contextuel associé au paquet où l'on désire
mettre le fichier de test avec l'item New > JUnit Test...
Par convention, on nomme MaClasseTest.java la classe de test pour la classe
MaClasse.java à tester.
III.2 Créer la classe à tester
Le menu contextuel à l'intérieur d'un fichier permet de naviguer entre le fichier
source et le fichier de test :
► Naviguate > Go to Source
► Naviguate > Go to Test/Tested class
ENSICAEN - Spécialité Informatique
La première visite du fichier source va créer le fichier source correspondant
dans le bon paquet.
III.3 Exécuter un test
Pour exécuter un fichier de test, il suffit d'utiliser le menu contextuel par un clic
droit sur le nom du fichier test dans le navigateur puis d'utiliser l'option Test
File. Pour exécuter tous les fichiers de test en une seule fois, il faut utiliser le
menu contextuel associé au nom du projet dans le navigateur et utiliser l'option
Test. Une fenêtre s'ouvre et permet de visualiser le résultat des tests :
III.4 Exemple introductif de cas de test
Le programme suivant utilise des méthodes statiques pour faire des calculs
arithmétiques. Toutes les méthodes à tester doivent être publiques ou protégées (si
Tests unitaires en TDD avec JUnit sous Netbeans ■ 3
le dossier test respecte l'organisation en paquet du fichier source), mais pas
privées, afin d'être testables.
1 public class StaticCalculation {
2
public static int add( int number1, int number2 ) {
3
return number1 + number2;
4
}
5
public static int sub( int number1, int number2 ) {
6
return number1 - number2;
7
}
8
public static int mul( int number1, int number2 ) {
9
return number1 * number2;
10
}
11
public static int div( int number1, int number2 ) {
12
return number1 / number2;
13
}
14 }
La classe de test est :
III.5 Annotations
La Table 1 donne un aperçu des annotations de JUnit 4.x.
Table 1: Annotations.
Annotation
@Test
Description
Marque une méthode comme une méthode de test.
public void whenCaseThenResult()
@Before
Exécute cette méthode avant chaque test. Elle permet de
public void setup()
préparer l'environnement du test, e.g. lire des valeurs,
initialiser la classe.
@After
Exécute cette méthode après chaque test.
public void tearDown()
@BeforeClass
Exécute cette méthode avant le début de tous les tests.
public static void setupBeforeClass()
Cela peut être utilisé pour faire des traitements
chronophages, par exemple pour se connecter à une base
de données.
@AfterClass
Exécute cette méthode après la série de tous les tests.
ENSICAEN - Spécialité Informatique
1 import static org.junit.Assert.assertEquals;
2 import org.junit.Test;
3
4 public class StaticCalculationTest {
5
@Test
6
public void addSubTest() {
7
assertEquals(3, StaticCalculation.add(1, 2));
8
assertEquals(1, StaticCalculation.sub(2, 1));
9
}
10
@Test
11
public void mulDivTest() {
12
assertEquals(6, StaticCalculation.mul(2, 3));
13
assertEquals(0.5, StaticCalculation.div(1, 2), 1e-8);
14
}
}
4 ■ Tests unitaires en TDD avec JUnit sous Netbeans
Annotation
Description
public static void tearDownAfterClass()
Cela permet de nettoyer l'environnement de test, par
exemple se déconnecter d'une base de données.
@Ignore
Ignore ce test. Utile par exemple quand le test n'est pas
public void method()
encore prêt.
@Test(expected=Exception.class)
Teste si la méthode lève bien l'exception attendue.
public void method()
@Test(timeout=100)
Fait échouer le test si la durée de traitement de la
public void method()
méthode est supérieure à 100 millisecondes.
III.6 Les formes de Assert
La Table 2 récapitule les principales assertions utilisables pour les tests.
Table 2: Liste des fonctions d'assertion.
Fonction
Description
ENSICAEN - Spécialité Informatique
assertTrue([message], condition)
Vérifie que la condition est vraie.
assertFalse([message], condition)
Vérifie que la condition est fausse.
assertsEquals([message], expected, actual)
Teste si les valeurs sont égales. Note : pour
les tableaux, seule la référence est vérifiée,
pas le contenu des tableaux.
assertsEquals([message], expected, actual, tolerance) Vérifie l'égalité entre des doubles ou des
flottants à une valeur de précision près.
assertNull([message], object)
Vérifie que l'objet est bien nul.
assertNotNull([message], object)
Vérifie que l'objet n'est pas nul.
assertSame([message], expected, actual)
Vérifie que les deux variables pointent vers le
même objet
assertNotSame([message], expected, actual)
Vérifie que les deux variables ne pointent pas
vers le même objet.
III.6.1 Utilisation de base
Cette section introduit les annotations de JUnit 4 à travers un exemple.
1 public class JUnitTest1 {
2
3
private Collection _collection;
4
5
@BeforeClass
6
public static void oneTimeSetUp() {
7
System.out.println("@BeforeClass - oneTimeSetUp");
8
}
9
10
@AfterClass
11
public static void oneTimeTearDown() {
12
System.out.println("@AfterClass - oneTimeTearDown");
13
}
14
15
@Before
16
public void setUp() {
Tests unitaires en TDD avec JUnit sous Netbeans ■ 5
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39 }
_collection = new ArrayList();
System.out.println("@Before - setUp");
}
@After
public void tearDown() {
_collection.clear();
System.out.println("@After - tearDown");
}
@Test
public void testEmptyCollection() {
assertTrue(_collection.isEmpty());
System.out.println("@Test - testEmptyCollection");
}
@Test
public void testOneItemCollection() {
_collection.add("itemA");
assertEquals(1, _collection.size());
System.out.println("@Test - testOneItemCollection");
}
@BeforeClass - oneTimeSetUp
@Before - setUp
@Test - testEmptyCollection
@After - tearDown
@Before - setUp
@Test - testOneItemCollection
@After - tearDown
40 @AfterClass - oneTimeTearDown
Les méthodes “@BeforeClass” et “@AfterClass” doivent être déclarées en tant
que méthodes statiques.
III.6.2 Tester des exceptions
Dans l'exemple suivant, le test a pour but de vérifier qu'il y a bien une
exception de type ArithmeticException qui est levée puisque qu'il y a une
division par 0.
1 public class JUnitTest2 {
2 @Test(expected = ArithmeticException.class)
3 public void divisionWithException() {
4
int i = 1/0;
5 }
6 }
III.6.3 Ignorer un test
L'annotation “@Ignore” signifie d'ignorer le test de la méthode. Cela est utile
par exemple lorsque la méthode n'est pas encore prête.
1 public class JUnitTest3 {
2
ENSICAEN - Spécialité Informatique
Résultat :
6 ■ Tests unitaires en TDD avec JUnit sous Netbeans
3
4
5
6
7
8 }
@Ignore("Not Ready to Run")
@Test
public void divisionWithException() {
System.out.println("Method is not ready yet");
}
III.6.4 Tester la durée d'une méthode
Le paramètre timeout de l'annotation “@Test” permet de tester la durée
maximale de l'exécution d'une méthode en millisecondes.
1 public class JUnitTest4 {
2
@Test(timeout = 1000)
3
public void infinity() {
4
while (true);
5
}
6 }
Dans l'exemple précédent, la méthode infinity() ne finira jamais, donc le
moteur JUnit la marque comme échec après 1000 millisecondes et lèvera
ENSICAEN - Spécialité Informatique
l'exception suivante :
java.lang.Exception: test timed out after 1000 milliseconds
III.6.5 Exécuter une suite de tests
Le moteur JUnit n'utilise aucun ordre a priori sur les tests à effectuer. Cela
suppose que les tests unitaires soient indépendants. Les annotations @RunWith
and @Suite permettent d'exécuter une suite de tests dans l'ordre spécifié.
L'exemple suivant enchaîne les tests JUnitTest1 et JUnitTest2 après que le test
JUnitTest5 ait été exécuté lui-même.
1
2
3
4
@RunWith(Suite.class)
@Suite.SuiteClasses({JUnitTest1.class, JUnitTest2.class})
public class JUnitTest5 {
}
Résultat :
BeforeClass - oneTimeSetUp
@Before - setUp
@Test - testEmptyCollection
@After - tearDown
@Before - setUp
@Test - testOneItemCollection
@After - tearDown
@AfterClass - oneTimeTearDown
III.6.6 Tests paramétrés
Les tests paramétrés permettent de varier les valeurs de paramètres pour les
tests.
5 @RunWith(Parameterized.class)
Tests unitaires en TDD avec JUnit sous Netbeans ■ 7
6 public class MyUnitTest {
7
// ...
8 }
La méthode marquée avec @parameters doit être statique et retourner une Collection. Les éléments ajoutés à cette collection doivent être transmis au constructeur du test unitaire comme argument. Cette partie est très importante.
1 @RunWith(Parameterized.class)
2 public class MyUnitTest {
3
private Integer _testNumber;
4
5
public MyUnitTest( Integer myTestNumber ) {
6
_testNumber = myTestNumber;
7
}
8
9
@Parameters
10
public static Collection testNumbers() {
11
return Arrays.asList(1, 2, 3);
12
}
13 }
La méthode annotée avec @parametrized peut évidemment être un générateur Et voici un cas de test paramétré :
1 @RunWith(Parameterized.class)
2 public class PrimeNumberValidatorTest {
3
private Integer _primeNumber;
4
private Boolean _expectedValidation;
5
private PrimeNumberValidator _primeNumberValidator;
6
7
@Before
8
public void initialize() {
9
_primeNumberValidator = new PrimeNumberValidator();
10
}
11
12
// Chaque parametre doit être placé comme argument
13
// A chaque exécution du moteur de test, il passe
14
// les valeurs comme parametres au contructeur.
15
public PrimeNumberValidatorTest(Integer primeNumber, Boolean
expected ) {
16
_primeNumber = primeNumber;
17
_expectedValidation = expected;
18
}
19
20
@Parameters
21
public static Collection primeNumbers() {
22
return Arrays.asList(new Object[][] {
23
{ 2, true }, { 6, false },
24
{ 19, true }, { 22, false }});
25
}
26
27
// Sera exécuté 4 fois puisque nous avons définis 4 paramètres
28
@Test
29
public void testPrimeNumberValidator() {
30
assertEquals(_expectedValidation,
31
_primeNumberValidator.validate(primeNumber));
ENSICAEN - Spécialité Informatique
de nombres aléatoires qui retourne une collection de nombres.
8 ■ Tests unitaires en TDD avec JUnit sous Netbeans
32
}
33 }
1 class PrimeNumberValidator {
2
public Boolean validate(final Integer primeNumber) {
3
for (int i = 2; i < (primeNumber / 2); i++) {
4
if (primeNumber % i == 0) {
5
return false;
6
}
7
}
8
return true;
9
}
10 }
ENSICAEN - Spécialité Informatique