0% ont trouvé ce document utile (0 vote)
37 vues29 pages

Tests Unitaires avec JUnit

Transféré par

melhaddad
Copyright
© © All Rights Reserved
Nous prenons très au sérieux les droits relatifs au contenu. Si vous pensez qu’il s’agit de votre contenu, signalez une atteinte au droit d’auteur ici.
Formats disponibles
Téléchargez aux formats PDF, TXT ou lisez en ligne sur Scribd
0% ont trouvé ce document utile (0 vote)
37 vues29 pages

Tests Unitaires avec JUnit

Transféré par

melhaddad
Copyright
© © All Rights Reserved
Nous prenons très au sérieux les droits relatifs au contenu. Si vous pensez qu’il s’agit de votre contenu, signalez une atteinte au droit d’auteur ici.
Formats disponibles
Téléchargez aux formats PDF, TXT ou lisez en ligne sur Scribd

Tests unitaires

avec JUnit

dernière modification 10/01/2024

Philippe Genoud
[Link]@[Link]

©
© Philippe
Philippe GENOUD
GENOUD UGA
UJF Janvier 2024
2012 1
Le problème du test

 Tous les programmeurs savent qu’ils doivent écrire des tests, peu le font correctement..

Moins de
Augmentation temps pour écrire
de la pression des tests
Je suis trop
à la bourre !

Baisse de Code moins


la productivité stable

© Philippe GENOUD UGA Janvier 2024 2


Tests du logiciel

 différents types de test


 test unitaire (ou test de composants)
 procédure permettant de vérifier le bon fonctionnement d'une partie précise d'un logiciel ou d'une portion d'un
programme (appelée « unité » ou « module »).
 en POO test du contrat de la classe (méthodes publiques et constructeurs)

 test d'intégration
 procédure visant à vérifier le bon fonctionnement d'un ensemble de composants formant tout ou partie d'un
logiciel : dans le test d’intégration, chacun des modules indépendants du logiciel est assemblé et testé dans
l’ensemble.
 test système (ou test fonctionnel)
 procédure visant à vérifier le bon fonctionnement du logiciel (système intégré) afin d'évaluer sa conformité aux
exigences spécifiées (Spécifications fonctionnelles et techniques des besoins)
 test de performance (benchmark)
 utilisé pour comparer les composants logiciels à plusieurs reprises. L'objectif est de garantir que le code testé
s'exécute assez rapidement, même s'il est soumis à une charge élevée.

© Philippe GENOUD UGA Janvier 2024 3


Test Unitaire d'une classe

 L'interfacepublique d'une classe définit


"un contrat" entre celui qui fournit la
classe et celui qui l'utilise.

 Ce contrat définit:
 les services proposés par la classe
 la manière dont ces services doivent être
utilisés

 Tester une classe consiste à vérifier la


validité de ce contrat
 il confronte la réalisation de la classe à sa
spécification.
 Tests unitaires (Unit Tests)
© Philippe GENOUD UGA Janvier 2024 4
Comment tester une classe ?
 Traces (instructions print) dans les programmes
[Link]

Implémentation
Spécification Trace d’exécution
Programme de test
Vérification

Spécification

Quels sont les problèmes d’une


telle approche ?
[Link]
© Philippe GENOUD UGA Janvier 2024 5
Comment tester une classe ?

 jugement humain
 Risques d’erreur humaine
 si une trace contient de nombreux print, perte
de lisibilité (Scroll Blindness)
 Processus long et répétitif à renouveler chaque
fois que la classe est modifiée et/ou qu'un bug
est corrigé (test de non régression)

Inefficace et coûteux

Automatiser les tests et les confier à des programmes

© Philippe GENOUD UGA Janvier 2024 6


JUnit

 JUnit : un framework open source ([Link]) pour le test


unitaire de programmes Java qui permet d’automatiser les tests.
 Facilite
 écriture des programmes de tests unitaires
 exécution des tests unitaires

 l'exploitation des résultats de test

 Terminologie JUnit
 Test unitaire (Unit test) : test d’une classe
 Cas de test (Test case) : teste les réponses d’une méthode à un ensemble
particulier d’entrées
 Suite de tests (Test suite) : une collection de cas de tests

 Testeur (Test runner) : programme qui exécute des suites de tests et


rapporte les résultats

© Philippe GENOUD UGA Janvier 2024 7


Mise en œuvre de tests avec JUnit
3 versions de JUnit
 JUnit 3.8
 Basé sur un framework de Classes et Interfaces
 Ecriture de tests en écrivant des classes s'intégrant au framework
Pour chaque test écriture
d’une méthode testXX

 JUnit 4  Accroche une information à un élément (classe, méthode,


attribut) Java
 Basé sur des annotations (Java 5+)  Utilisée par un mécanisme tiers (compilateur java,
environnement d'exécution…) … et non par la classe où
 Ces annotations permettent de marquer les classes et elle est définie

méthodes de test @Test


 Nouvelles fonctionnalités et plus de souplesse (plus public void testMult() {
...
de contraintes d'héritage) }

 JUnit 5 (Septembre 2017)

© Philippe GENOUD UGA Janvier 2024 8


Exemple : test unitaire de la classe Rational
Pour réaliser les tests
unitaires d'une classe création
d'une classe de test séparée
Rational la classe à tester

RationalTest la classe de test


contenant les tests unitaires de
Rational

En général les classes de test se situent


dans un projet différent ou dans un
dossier source différent pour séparer le
code effectif de l'application du code de
test

organisation des
sources dans un
projet maven

© Philippe GENOUD UGA Janvier 2024 9


JUnit 5 : Ecriture d’une classe de Test
 La classe import [Link];
de test : import [Link];
static void assertEquals(int expected,
public class RationalTest { int actual)

//… méthode statique de [Link]


Cas de test pour la • vérifie que le premier paramètre (valeur attendue) est égal au
méthode mult de la deuxième paramètre (valeur calculée)
@Test
classe Rational • Si la valeur calculée n’est pas égale à la valeur attendue lance
public void testMult() { une exception (en fait une Error) de type
Création des objets AssertionFailedError
qui vont interagir lors Rational r1 = new Rational(2, 3);
du test Rational r2 = new Rational(3, 5);
Code qui agit sur les
objets impliqués dans [Link](r2);
le test
Vérification que le [Link](2, [Link]());
résultat obtenu [Link](5, [Link]());
correspond bien au }
résultat attendu. }

© Philippe GENOUD UGA Janvier 2024 10


JUnit 5 : Ecriture d’une classe de Test
 La classe import [Link]; utilisation d'un import static pour 'simplifier' les appels des méthodes d'assertion

de test : static [Link].*;


import [Link];
static void assertEquals(int expected,
public class RationalTest { int actual)

//… méthode statique de [Link]


Cas de test pour la • vérifie que le premier paramètre (valeur attendue) est égal au
méthode mult de la deuxième paramètre (valeur calculée)
@Test
classe Rational • Si la valeur calculée n’est pas égale à la valeur attendue lance
public void testMult() { une exception (en fait une Error) de type
Création des objets AssertionFailedError
qui vont interagir lors Rational r1 = new Rational(2, 3);
du test Rational r2 = new Rational(3, 5);
Code qui agit sur les
objets impliqués dans [Link](r2);
le test
assertEquals(2, [Link]());
[Link](14, [Link]());
Vérification que le inutile de préfixer les méthodes par le nom de la classe
résultat obtenu assertEquals(5, [Link]());
[Link](15, [Link]());
correspond bien au }
résultat attendu. }

© Philippe GENOUD UGA Janvier 2024 11


JUnit 5 : Exécution d’une classe de Test
 La classe import [Link];
La classe de test est exécutée par le TestRunner
de test : import static [Link].*;
de JUnit
public class RationalTest {
chaque cas de test est exécuté indépendamment
//… des autres et un rapport de tests est produit.
Cas de test pour la
méthode mult de la @Test pour chaque cas de test 3 résultats sont possibles
classe Rational public void testMult() {
Cas de Test réussi :
Rational r1 = new Rational(2, 3); toutes les assertions sont vérifiées
Rational r2 = new Rational(3, 5);

[Link](r2); Failure:
le code ne correspond pas au critères de test, une
assertEquals(2, [Link]()); assertion n’est pas vérifiée
assertEquals(5, [Link]());
}
}

Erreur: une situation imprévue est intervenue dans


Exécution automatique Rapport de tests l'exécution du test - une exception a été levée
par l’exécuteur de tests
JUnit

© Philippe GENOUD UGA Janvier 2024 12


JUnit 5 : Les différentes méthodes assert
La classe Assertions du package [Link] fourni de nombreuses
méthodes statiques pour définir des conditions de test
[Link]

 assertEquals(Object expected, Object actual)


assertEquals(int expected, int actual) Méthodes surchargées pour tous les types primitifs
assertEquals(float expected, float actual)
...
assertEquals(float expected, float actual, float delta) Lorsque assertEquals porte sur des valeurs float ou double possibilité spécifier un delta qui
représente un seuil de différence entre les deux valeurs
...
assertEquals(Object expected, Object actual, String message) Acceptent un éventuel paramètre message fournissant une description
assertEquals(int expected, int actual, String message) textuelle documentant l'erreur et facilitant le
diagnostic des causes d'échec
assertEquals(int expected, int actual, String message)
...
assertEquals(Object expected, Object actual, Supplier<String> messageSupplier) Le message peut éventuellement être fourni par un
assertEquals(int expected, int actual, Supplier<String> messageSupplier) messageSupplier qui ne sera évalué qu'en cas
assertEquals(int expected, int actual, Supplier<String> messageSupplier) d'échec (failure)
...
 Vérifie que la valeur attendue (expected) est égale à la valeur effective (actual). Pour les objets, utilise la méthode equals héritée de Object*,
sinon pour les types primitifs utilise ==.
 Si les deux valeurs ne sont pas égales une AssertionFailedError est provoquée.
* c’est la méthode de signature public boolean equals(Object o) qui est appelée

© Philippe GENOUD UGA Janvier 2024 13


JUnit 5 : Les différentes méthodes assert
 assertSame(Object expected, Object actual)
assertSame(Object expected, Object actual, String message)
assertSame(Object expected, Object actual, Supplier<String> messageSupplier)
 Vérifie que expected et actual référencent le même objet (==), sinon une AssertionFailedError est provoquée.

 assertNotSame(Object expected, Object actual)


assertNotSame(Object expected, Object actual, String message)
assertNotSame(Object expected, Supplier<String> messageSupplier)
 Vérifie que expected et actual ne référencent pas le même objet (!=), sinon une AssertionFailedError est provoquée.

 assertNull(Object object)
assertNull(Object object, String message)
assertNull(Object object, Supplier<String> messageSupplier)
 Vérifie que object == null, sinon une AssertionFailedError est provoquée.

 assertNotNull(Object object)
assertNotNull(Object object , String message)
assertNotNull(Object object , Supplier<String> messageSupplier)
 Vérifie que la référence object n’est pas la valeur null, sinon une AssertionFailedError est provoquée.

© Philippe GENOUD UGA Janvier 2024 14


JUnit 5 : Les différentes méthodes assert
 static void assertTrue(boolean test)
static void assertTrue(boolean test, String message)
static void assertTrue(boolean test, Supplier<String> messageSupplier)
 Vérifie que test == true sinon une AssertionFailedError est provoquée.

 static void assertFalse(boolean test)


static void assertFalse(boolean test, String message)
static void assertFalse(boolean test, Supplier<String> messageSupplier)
 Vérifie que test == false, sinon une AssertionFailedError est provoquée.

 assertArrayEquals compare le contenu d'un tableau réel à un tableau attendu.


 assertLinesMatch compare deux listes de Strings.
 assertTimeout vérifie that que la function fournie se termine avant le timeout spécifié
 …

 fail()
fail(String message)
fail(Supplier<String> messageSupplier)
 Provoque l’échec du test et lance une AssertionFailedError

 Utile lorsque les autres méthodes assert ne correspondent pas exactement à vos besoins ou pour tester que certaines exceptions sont bien lancées.

© Philippe GENOUD UGA Janvier 2024 15


JUnit 5: tester les exceptions
"Verifying that code completes normally is only part of programming. Making sure the code behaves as expected in exceptional situations is part of the craft
of programming too. » JUnit Cookbook Kent Beck, Erich Gamma

 Comment vérifier que des exceptions sont lancées comme prévu ? à la JUnit 3.8

@Test try {
public void testDenomNull() { // appel d’une méthode devant lancer une Exception
...
try { // si l'exception n'a pas eu lieu on force le
//test échouer
new Rational(3,0);
fail("Did not throw an ExpectedException");
fail("IllegalArgumentException non lancée"); }
catch (ExpectedException e) {
} }
catch (IllegalArgumentException e) {
// OK exception lancée
}
}

Vérifie que l'exception est bien levée La classe de Expression lambda contenant le code à
et renvoie l'objet exception l'exception exécuter provoquant l'exception

@Test
public void testDenomNull() {
IllegalArgumentException except = assertThrows([Link], () -> new Rational(10, 0) );
assertEquals("dénominateur nul interdit", [Link]());
}

© Philippe GENOUD UGA Janvier 2024 16


JUnit5 : factoriser du code entre les méthodes de test
import [Link];
import [Link];
import [Link];
import static [Link].*;

public class TestRational {


// ...
private Rational r1;
@Test private Rational r2;
public testMult() {
Code exécuté avant chaque méthode de test
Rational r1 = new Rational(2, 3); @BeforeEach
Rational r2 = new Rational(7, 5);
[Link](r2); public void setUp() {
assertEquals(14,[Link]()); r1 = new Rational(2, 3);
assertEquals(15,[Link]()); r2 = new Rational(7, 5);
} }

@Test
public void testAdd() {
Code exécuté après chaque méthode de test
Rational r1 = new Rational(2, 3); @AfterEach
Rational r2 = new Rational(7, 5);
[Link](r2); public void tearDown() {
assertTrue(31 == [Link]()); ...
assertTrue(15 == [Link]()); ...
} }
}

© Philippe GENOUD UGA Janvier 2024 17


JUnit5 : factoriser du code entre les méthodes de test
import [Link];
import static [Link].*;
public class TestCounter { setUpClass()

setUp()
@BeforeAll
Code exécuté une seule fois avant de public static void setUpClass() {
... m1()
commencer l’exécution des méthodes de
}
test de la classe
tearDown() Ordre d’exécution
@AfterAll
Code exécuté une seule fois après avoir public static void tearDownClass() {
... setUp()
exécuté toutes les méthodes de test de la
}
classe m2()
@BeforeEach
public void setUp() { tearDown()
Code exécuté avant chaque méthode de ...
test de la classe }
tearDownClass()
@AfterEach
public void tearDown() {
Code exécuté après chaque méthode de
...
test de la classe }

@Test
public void m1() {
méthode de test ...
}

@Test
public void m2() {
On ne peut préjuger d’un ordre
méthode de test ... d’exécution des méthodes de test
}
} (@Test)

© Philippe GENOUD UGA Janvier 2024 18


JUnit5 : Exécution d’une classe de test
 en mode texte sur la console
Commande maven, qui efface tout ce qui a été
construit, recompile et lance les tests

De nombreux IDE intègrent JUnit : NetBeans, Eclipse…

nombre de méthodes de test ayant échoué à


cause d'une exception levée et non traitée

nombre de méthodes nombre de méthodes de test ayant échoué (assertion non


de test exécutées vérifiée ,méthode fail exécutée, exception levée)
© Philippe GENOUD UGA Janvier 2024 19
JUnit 5 : Exécuter un ensemble de tests
 dans JUnit 5 possibilité de définir des suites de tests (TestSuite) pour exécuter ensemble un ensemble
de tests en agrégeant plusieurs classes de test.
 JUnit 5 fourni les annotations suivantes :
 @SelectPackages – pour spécifier les noms des packages pour la suite de tests
 @SelectClasses – pour spécifier les classes pour la suite de tests. elles peuvent être situées dans différents packages.

 un exemple utilisant les deux annotations pour sélectionner deux classes de test et un package:
[Link]
package [Link];

import [Link];
import [Link];
import [Link]; classes dont tous les tests sont à exécuter
import [Link];
import [Link]; ces classes peuvent elles-mêmes
être des suites de de tests

@RunWith([Link])
@SelectClasses( { [Link], [Link] } )
@SelectPackages("[Link]")
public class TestSuiteDemo { package dont toutes les classes de test sont à exécuter
}

© Philippe GENOUD UGA Janvier 2024 20


Couverture de code

 comment mesurer la qualité des test effectués ?


 un indicateur de la qualité des tests effectués peut être la couverture de code (code
coverage)
 une mesure utilisée en génie logiciel pour décrire le taux de code source testé d'un programme.
 nombreuses méthodes pour mesurer la couverture de code. Les principales sont :
 Couverture des fonctions (Function Coverage)
Chaque fonction dans le programme a-t-elle été appelée ?
 Couverture des instructions (Statement Coverage)
Chaque ligne du code a-t-elle été exécutée et vérifiée ?
 Couverture des points de tests (Condition Coverage)
Chaque point d'évaluation (tel que le test d'une variable) a-t-il été exécuté et vérifié ? (Le point de test teste-t-il
ce qu'il faut ?)
 Couverture des chemins d'exécution (Path Coverage)
Chaque parcours possible (par exemple les 2 cas vrai et faux d'un if) a-t-il été exécuté et vérifié ?

© Philippe GENOUD UGA Janvier 2024 21


Couverture de code
 Trèssouvent en complément d'un framework de tests unitaires on utilise des outils de
couverture de code
 JaCoCo Java Code Coverage Library [Link]
 Intégration dans les IDE, ou avec maven
EclEmma for Eclipse [Link]

 d'autres outils Java


 Clover [Link]

 OpenClover [Link]

 Cobertura [Link]

….

© Philippe GENOUD UGA Janvier 2024 22


JUnit Testing tips (JUnit primer)
“Any program feature without an automated test simply doesn’t exist.”
Extreme Programming Explained, Kent Beck

 Code a little, test a little, code a little, test a little . . .


 Run your tests as often as possible, at least as often as you run the compiler 
 Begin by writing tests for the areas of the code that you’re the most worried about . . .write tests that have the
highest possible return on your testing investment
 When you need to add new functionality to the system, write the tests first
 If you find yourself debugging using [Link](), write a test case instead
 When a bug is reported, write a test case to expose the bug
 Don’t deliver code that doesn’t pass all the tests

 TDD Test Driven Development : écrire les tests avant les implémentations des classes

 Intégration continue – Pendant le développement, le programme marche toujours – peut être qu’il ne fait pas
tout ce qui est requis mais ce qu’il fait il le fait bien.
[Link]

© Philippe GENOUD UGA Janvier 2024 23


JUnit et les autres
xUnit famille de frameworks pour le test unitaire automatisés.
 Disponibles pour de nombreux langages et environements:
 JUnit. (Java) [Link]

 TestNG (java) [Link]


[Link]

cppUnit (C++). [Link]


 nUnit (.NET) [Link]

...

D’autres outils

 couverture de code (JaCoco , clover …)

 SeleniumHQ (test applications Web)


outil de profiling intégré à NetBens
 [Link]
 [Link]

 outils de profiling
 [Link]
 JProfiler [Link]

 VisualVM [Link]

 …

© Philippe GENOUD UGA Janvier 2024 24


© Philippe GENOUD UGA Janvier 2024 25
Références
 Le site JUnit : [Link]

 JUnit 5 Tutorial: Writing Assertions With JUnit 5 Assertion API


[Link]

 Maven – JaCoCo code coverage example, mkyong , published November 15, 2018
[Link]

 Junit5 – Développons en Java J.M. DOUDOUX


[Link]

 Le site [Link]

 eXtreme Programming Explained: Embrace Change & Test Driven Design


 by Cynthia Andres, Kent Beck
 Publisher: Addison-Wesley Professional
 Release Date: November 2004
 ISBN: 0321278658
© Philippe GENOUD UGA Janvier 2024 26
© Philippe GENOUD UGA Janvier 2024 27
© Philippe GENOUD UGA Janvier 2024 28
JUnit 3/4: tester les exceptions
"Verifying that code completes normally is only part of programming. Making sure the code behaves as expected in exceptional situations is part of the craft
of programming too. » JUnit Cookbook Kent Beck, Erich Gamma

 Comment vérifier que des exceptions sont lancées comme prévu ? à la JUnit 3.8

@Test try {
public void testDenomNull() { // appel d’une méthode devant lancer une Exception
...
try { // si l'exception n'a pas eu lieu on force le
//test échouer
new Rational(3,0); fail("Did not throw an ExpectedException");
fail("IllegalArgumentException non lancée"); }
catch (ExpectedException e) { }
}
catch (IllegalArgumentException e) {
// OK exception lancée
}
} Avec JUnit4 paramètre expected Le test échoue si l'exécution de la méthode dépasse le
de l'annotation @Test temps fixé par le paramètre timeout

@Test (expected = [Link]) @Test(timeout=10)


public void testDenomNull() { public void uneMéthode() {
new Rational(3,0); ...
} }

© Philippe GENOUD UGA Janvier 2024 32

Vous aimerez peut-être aussi