C306 Tests Unitaires

Télécharger au format pdf ou txt
Télécharger au format pdf ou txt
Vous êtes sur la page 1sur 6

C306: Tests unitaires https://pedag.u-picardie.fr/moodle/upjv/mod/page/view.php?

id=7736

C306 - Ingénierie du logiciel (ISI_05)


Accueil / Mes cours / FOAD / E-Miage / MASTER 1 MIAGE A DISTANCE [M1MIAA211] - 2021 / C306 - Ingénierie du logiciel (ISI_05)

/ Cours / Tests unitaires

Tests unitaires

1 - Introduction
Pour être sûr que le code fait bien ce qu'on en attend, il faut le tester. Pour cela, il faut écrire des tests dont les résultats sont connus et qui
éprouveront le code.

Exemple

1 function int min(int a,int b){ ?


2 if ( a < b )
3 return a;
4 return b;
5 }
6 // test
7 if ( 3 == min(3,5) ) {
8 // c'est bon
9 }

Les tests appliqués aux fonctions/méthodes sont appelés "tests unitaires". Pour valider les fonctions, il faut donc créér une batterie de tests
unitaires. Si les tests sont complets, il est aisement possible de modifier le code ("refactoring") sans crainte de mettre en péril le projet (en
assurant la "non-regression"). La solidité du projet tient donc dans l'exaustivité des tests. Dans l'idéal, les tests unitaires sont écrits avant
l'implémentation des fonctions (Test Driven Development/développement piloté par les tests).

Exemple

1 function int max(int a,int b) { ?


2 // TODO
3 }
4 if ( 5 == max(3,5) ) {
5 // c'est bon
6 }

Associé aux outils de couverture de code (qui permettent de signaler les parties de code non atteint par une exécution), les tests complets
permettent aussi de fournir les parties de code qui ne sont pas accessibles (ou accédées ?).

Comme il est difficile voir souvent impossible d'avoir des tests qui couvrent tous les cas possibles, il est important d'être très attentif à leur
écriture et de n'avoir que des fonctions simples (donc faciles à tester).

2 - Notions à assimiler
• Test unitaire

“ Le test unitaire est une 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 »). (Wikipédia, l'encyclopédie libre)”

• Non-régression

“La non-régression consiste à ce que des corrections de bogues ou ajout de


fonctionnalités nouvelles dans un logiciel ne provoquent pas une perturbation
de fonctions existantes par effet secondaire. (Wikipédia, l'encyclopédie libre)”

• Refactorisation / Refactoring

1 sur 6 30/05/2022, 17:22


C306: Tests unitaires https://pedag.u-picardie.fr/moodle/upjv/mod/page/view.php?id=7736

“Le réusinage de code (parfois nomme refactorisation - anglicisme venant de


refactoring) consiste à retravailler le code source d'un code informatique sans
lui ajouter de fonctionnalité au logiciel ni corriger de bogues, mais en
améliorant sa lisibilité pour simplifier sa maintenance, ou le rendre plus
générique (par exemple afin de faciliter le passage de simple en multiple
précision); (Wikipédia, l'encyclopédie libre)”

Par exemple modifier le code pour en améliorer les performances correspond à une "refactorisation".

• Test Driven Development

“Le Test Driven Development (TDD) ou en Français développement piloté par les
tests est une technique de développement de logiciel qui préconise d'écrire les
tests unitaires avant d'écrire le code source d'un logiciel. (Wikipédia,
l'encyclopédie libre)”

3 - Tests unitaires avec JUnit


L'écriture de tests unitaires en Java peut se faire simplement avec JUnit5. JUnit5 s'intègre facilement dans beaucoup d'environnement de
développement (via maven, gradle ,...); pour tester en utilisant le minimum d'outil pour mieux comprendre, nous allons utiliser la version
'console' de JUnit5, avec la jar : junit-platform-console-standalone-<version>.jar (version au moment de l'écriture de ce texte: junit-
platform-console-standalone-1.5.0-RC2.jar)

https://junit.org/junit5/docs/current/user-guide/#running-tests-console-launcher

3.1 - Méthode pour les tests JUnit


Plusieurs méthodes sont fournies pour décrire les tests et le résulat qui en est attendu: par exemple

assertEquals(4,min(4,5)); ?

sera valide si le résultat de l'appel à la méthode 'min(4,5)' correspond au résultat attendu '4'.

Exemple de méthodes pour les tests

assertFalse // teste si une valeur est fausse ?


assertNotNull // teste si une valeur est non null
assertNull // teste si une valeur est null
assertEquals // test si 2 valeurs ou objets sont égales
...

Si l'exécution du test conduit à un état non valide, on peut aussi utilisé la méthode 'fail' pour déclencher la non validation du test (voir la partie
sur les exceptions).

fail("on ne devrait pas être ici"); ?

Toutes les informations utiles sur ces méthodes se trouve dans la 'Javadoc' de JUnit

https://junit.org/junit5/docs/current/api/org/junit/jupiter/api/Assertions.html
3.2 - Ecriture d'un test JUnit
Le test peut être écrit directement dans la classe, avec l'annotation @Test précédent la méthode de test.

1 @Test ?
2 public void testMin() {
3 assertEquals(4,min(4,5));
4 }

Cette méthode de test est écrite dans une classe java qui sera dédiée à un jeu de tests:

1 import static org.junit.jupiter.api.Assertions.assertEquals; // import pour la méthode statique assertEquals ?


2 import org.junit.jupiter.api.Test; // import pour l'annotation @Test indiquant qu'il s'agit d'une méthode de test
3
4 public class TestFoo {
5 @Test
6 public void testMin() {
7 assertEquals(4,Foo.min(4,5)); // min est une méthode statique de la classe Foo
8 }
9 }

3.3 - Exécution d'un test réussi


Exemple

2 sur 6 30/05/2022, 17:22


C306: Tests unitaires https://pedag.u-picardie.fr/moodle/upjv/mod/page/view.php?id=7736

1 import static org.junit.jupiter.api.Assertions.assertEquals;


2 import org.junit.jupiter.api.Test;
3
4 public class TestFoo {
5 @Test
6 public void testMin() {
7 assertEquals(4,Foo.min(4,5));
8 }
9 }

1 /** ?
2 * Classe d'exemple
3 * @author [email protected]
4 */
5 public class Foo {
6
7 public static int min(int a,int b){
8 if ( a < b )
9 return a;
10 return b;
11 }
12
13 }

Compilation, nous allons mettre le code compilé dans le dossier 'out'

Compilation de la classe :

> javac -d out Foo.java ?

Compilation de la classe de tests :

> javac -d out -cp junit-platform-console-standalone-1.5.0-RC2.jar:out TestFoo.java ?

N.B.: sous windows, le séparateur de dossier dans le "classpath" est ';' et non ':', il faudrait donc faire :

> javac -d out -cp junit-platform-console-standalone-1.5.0-RC2.jar;out TestFoo.java ?

Exécution des tests: (il va prendre toutes les classes qui contiennent 'Test')

> java -jar junit-platform-console-standalone-1.5.0-RC2.jar -cp out --scan-classpath ?

Thanks for using JUnit! Support its development at https://junit.org/sponsoring


├─ JUnit Jupiter ✔
│ └─ TestFoo ✔
│ └─ testMin() ✔
└─ JUnit Vintage ✔

Test run finished after 91 ms


[ 3 containers found ]
[ 0 containers skipped ]
[ 3 containers started ]
[ 0 containers aborted ]
[ 3 containers successful ]
[ 0 containers failed ]
[ 1 tests found ]
[ 0 tests skipped ]
[ 1 tests started ]
[ 0 tests aborted ]
[ 1 tests successful ]
[ 0 tests failed ]

Toutes les méthodes annotées "@Test" ont été exécutées (ici 1), le résultat est "successful"

3.4 - Exécution de test non validés


Exemple

1 import static org.junit.jupiter.api.Assertions.assertEquals; // import pour la méthode statique assertEquals ?


2 import org.junit.jupiter.api.Test; // import pour l'annotation @Test indiquant qu'il s'agit d'une méthode de test
3
4 public class TestFoo {
5
6 @Test
7 public void testMin() {
8 assertEquals(4,Foo.min(4,5));
9 }
10
11 @Test
12 public void testMin2() {
13 assertEquals(2,Foo.min(4,5));
14 }
15
16 }

Recompiilation

> javac -d out -cp junit-platform-console-standalone-1.5.0-RC2.jar:out TestFoo.java ?

3 sur 6 30/05/2022, 17:22


C306: Tests unitaires https://pedag.u-picardie.fr/moodle/upjv/mod/page/view.php?id=7736

Exécution du test

> java -jar junit-platform-console-standalone-1.5.0-RC2.jar -cp out --scan-classpath ?

Thanks for using JUnit! Support its development at https://junit.org/sponsoring


├─ JUnit Jupiter ✔
│ └─ TestFoo ✔
│ ├─ testMin() ✔
│ └─ testMin2() ✘ expected: <2> but was: <4>
└─ JUnit Vintage ✔

Failures (1):
JUnit Jupiter:TestFoo:testMin2()
MethodSource [className = 'TestFoo', methodName = 'testMin2', methodParameterTypes = '']
=> org.opentest4j.AssertionFailedError: expected: <2> but was: <4>
org.junit.jupiter.api.AssertionUtils.fail(AssertionUtils.java:55)
org.junit.jupiter.api.AssertionUtils.failNotEqual(AssertionUtils.java:62)
org.junit.jupiter.api.AssertEquals.assertEquals(AssertEquals.java:150)
org.junit.jupiter.api.AssertEquals.assertEquals(AssertEquals.java:145)
org.junit.jupiter.api.Assertions.assertEquals(Assertions.java:510)
TestFoo.testMin2(TestFoo.java:17)
java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
java.base/java.lang.reflect.Method.invoke(Method.java:566)
[...]

Test run finished after 107 ms


[ 3 containers found ]
[ 0 containers skipped ]
[ 3 containers started ]
[ 0 containers aborted ]
[ 3 containers successful ]
[ 0 containers failed ]
[ 2 tests found ]
[ 0 tests skipped ]
[ 2 tests started ]
[ 0 tests aborted ]
[ 1 tests successful ]
[ 1 tests failed ]

Le test n'a pas été validé ("failed"), en retour JUnit renvoie une erreur donnant le résultat qui était attendu (2) et le résultat obtenu (4) ainsi que
la trace d'exécution de l'erreur.

3.5 - Tester les levées d'exceptions


Lorsqu'une méthode doit renvoyer une exception (par exemple parce que un paramètre n'est pas correct et qu'il est prévu d'une exception
soit renvoyée dans ce cas), il est aussi important de tester cette fonctionnalité.

Cela peut être réalisé avec les instructions de gestion des exceptions try {} catch{} : on provoque l'exception dans le bloc 'try', le résultat
attendu étant que l'exception soit levée, l'exécution doit se poursuivre dans le bloc 'catch' et non continuer dans la suite du bloc "try".
Ainsi un appel à 'fail' après l'instruction ayant dû provoquer l'exception mettra le test en échec si celle-ci n'est pas levée.

Exemple

1 public class DivisionParZero { ?


2 /**
3 * Calcul la division de deux nombres
4 * @param a entier
5 * @param b entier
6 * @return a / b
7 * @throws IllegalArgumentException si b vaut 0
8 **/
9 public static int div(int a,int b){
10 if ( b == 0 ) {
11 throw new IllegalArgumentException("b ne doit pas valoir 0");
12 }
13 return a / b;
14 }
15 }

1 import static org.junit.jupiter.api.Assertions.fail; ?


2 import org.junit.jupiter.api.Test;
3
4 public class TestDivisionParZero {
5
6 @Test
7 public void testDivZero() {
8 try {
9 DivisionParZero.div(3,0);
10 fail("l'exception pour la division par zero aurait dû être levée");
11 } catch(IllegalArgumentException e) {
12 // rien a faire, c'est normal qu'on ai l'exception
13 }
14 }
15 }

Exécution du test

4 sur 6 30/05/2022, 17:22


C306: Tests unitaires https://pedag.u-picardie.fr/moodle/upjv/mod/page/view.php?id=7736

> java -jar junit-platform-console-standalone-1.5.0-RC2.jar -cp out --scan-classpath ?

Thanks for using JUnit! Support its development at https://junit.org/sponsoring


├─ JUnit Jupiter ✔
│ └─ TestDivisionParZero ✔
│ └─ testDivZero() ✔
└─ JUnit Vintage ✔

Test run finished after 92 ms


[ 3 containers found ]
[ 0 containers skipped ]
[ 3 containers started ]
[ 0 containers aborted ]
[ 3 containers successful ]
[ 0 containers failed ]
[ 1 tests found ]
[ 0 tests skipped ]
[ 1 tests started ]
[ 0 tests aborted ]
[ 1 tests successful ]
[ 0 tests failed ]

4 - Exercices
Exercice
Ecrire un test permettant de contrôler le bon fonctionnement de la méthode suivante. La question ici n'est pas d'implémenter la méthode mais
d'écrire les tests.

1 /** ?
2 * Calcul le PGCD (Plus Grand Denominateur Commun)
3 * de 2 nombres positifs ou nul.
4 *
5 * @param a premier entier
6 * @param b second entier
7 * @return pgcd de a et b
8 * @throws IllegalArgumentException si a ou b est strictement négatif
9 **/
10 public static int pgcd(int a,int b){
11 // TODO
12 }

Solution

1 @Test ?
2 public void testPgcd() {
3 assertEquals(8,pgcd(24,16));
4 assertEquals(10,0,10);
5 assertEquals(0,0,0);
6 }
7
8 @Test
9 public void testPgcdArguments1() {
10 try {
11 pgcd(1,-1);
12 fail("exception pas detectée sur a");
13 } catch (IllegalArgumentException e) {
14 // ok
15 }
16 }
17
18 @Test
19 public void testPgcdArguments2() {
20 try {
21 pgcd(-1,1);
22 fail("exception pas detectée sur b");
23 } catch (IllegalArgumentException e) {
24 // ok
25 }
26 }

Conclusion
Les tests permettent un contrôle automatisé du fonctionnement attendu des fonctions/méthodes. Pour pouvoir facilement écrire les tests, il
est préférable de penser aux tests avant l'implémentation.

Les exemple ci-dessus manipulaient des méthodes statiques, mais rien n'empêche de reproduire la même chose avec des instances de classes
et leurs méthodes:

5 sur 6 30/05/2022, 17:22


C306: Tests unitaires https://pedag.u-picardie.fr/moodle/upjv/mod/page/view.php?id=7736

1 public class Calculateur {


2 /**
3 * Calcul la division de deux nombres
4 * @param a entier
5 * @param b entier
6 * @return a / b
7 * @throws IllegalArgumentException si b vaut 0
8 **/
9 public int div(int a,int b){
10 if ( b == 0 ) {
11 throw new IllegalArgumentException("b ne doit pas valoir 0");
12 }
13 return a / b;
14 }
15 }

1 import static org.junit.jupiter.api.Assertions.fail; ?


2 import static org.junit.jupiter.api.Assertions.assertEquals;
3 import org.junit.jupiter.api.Test;
4
5 public class TestCalculateur {
6
7 Calculateur calc = new Calculateur();
8
9 @Test
10 public void testDivision() {
11 assertEquals(2,calc.div(10,4));
12 }
13
14 @Test
15 public void testDivisionParZero() {
16 try {
17 calc.div(3,0);
18 fail("l'exception pour la division par zero aurait dû être levée");
19 } catch(IllegalArgumentException e) {
20 // rien a faire, c'est normal qu'on ai l'exception
21 }
22 }
23 }

Modifié le: vendredi 12 juillet 2019, 10:32

◄ Modularité

Aller à…

Couverture de code ►

Connecté sous le nom « SODO KANGNI KOMLAN » (Déconnexion)


C306
Mes cours
ı Événements
ı Aide
ı
Accompagnement enseignants
ı Etudiants Maîtrisez votre environnement numérique
ı Enseignants Préparez vos examens à distance
ı Learning Hub
ı Bac à sable
ı Moodle Epione
ı Français (fr)
ı
English (en)
ı Français (fr)
Résumé de conservation de données
Obtenir l'app mobile

UNIVERSITÉ PICARDIE JULES VERNE ı Contact ı Charte informatique ı CGU

6 sur 6 30/05/2022, 17:22

Vous aimerez peut-être aussi