SlideShare a Scribd company logo
1 of 21
Download to read offline
BBL sur les tests
JUnit, Mockito, AssertJ
Une petite soif ? Sirotez donc un mockito (sans alcool bien sûr :p) en écoutant CE BBL!
Par Idriss Neumann, le 11/02/2016
Sommaire
❖ Généralités
➢ Pourquoi écrire des tests ?
➢ Différence entre TI et TU
➢ Démarche du TDD
❖ Le framework JUnit
➢ Définition
➢ Structure d’un test case
➢ Les différentes assertions
➢ Assertions sur exception
❖ L’alternative AssertJ
❖ Les objets simulacres
➢ Différences entre mock et stub
➢ Liste des mocking toolkits
❖ Le framework Mockito
➢ Créer un mock
➢ Prédire le résultat d’une méthode d’un mock
➢ Faire des assertions sur l’invocation d’un mock
➢ L’utilisation des annotations
➢ Les arguments captors
➢ Les espions (spy)
❖ Question
Généralités
Pourquoi écrire des tests ?
Écrire des scénarios de tests automatisés (tant des tests unitaires que des tests d'intégration) constitue une étape incontournable
du cycle de développement logiciel dans un cadre professionnel.
Bien que cette tâche puisse parfois sembler rébarbative, elle est indispensable pour produire des développements de bonne
qualité, notamment parce que cela permet entre autres de :
❖ s'assurer que l'on maîtrise les aspects fonctionnels de l'application (les besoins métier à satisfaire) ;
❖ détecter au plus tôt les anomalies éventuelles avant la livraison d'un projet aux utilisateurs ;
❖ détecter d'éventuelles régressions, suite à l'implémentation de nouvelles fonctionnalités, en automatisant l'exécution de
l'ensemble de tous les scénarios de tests implémentés sous forme de tests unitaires ou tests d'intégration, de façon à ce
qu'ils soient exécutés régulièrement (à minima une fois par jour par exemple).
Plus l'ensemble de vos codes sera couvert par les tests, plus vous serez à même de détecter les régressions, de maîtriser
l'ensemble des fonctionnalités de votre application et ainsi de la rendre plus maintenable.
Généralités
Différence entre tests unitaires et tests d’intégration
De manière générale, le test unitaire a pour but de tester une partie précise d'un logiciel (d'où le nom "unitaire") voire une
fonction précise, en essayant de couvrir au maximum la combinatoire des cas fonctionnels possibles sur cette portion de code.
Cependant, cela ne suffit pas à différencier un test unitaire d'un test d'intégration qui, lui aussi, peut se contenter de tester une
portion précise du code.
La différence fondamentale est que le test unitaire doit uniquement tester le code et ne doit donc pas avoir d'interactions avec
des dépendances externes du module testé telles que des bases de données, des Web services, des appels à des procédures
distantes, la lecture ou écriture de fichiers ou encore d'autres fonctions. Les dépendances devront être simulées à l'aide de
mécanismes tels que les bouchons ou encore les mocks.
Concernant les tests d'intégration, ceux-ci permettent, de manière générale, de vérifier la bonne intégration de la portion de code
testée dans l'ensemble du logiciel et de ses dépendances. Il peut s'agir tout aussi bien de vérifier la bonne insertion de données
dans un SGBDR sur une petite portion de code que de scénarios complets du logiciel, correspondant à des enchaînements
d'appels de fonctions, de Web services... (c'est ce que l'on appelle aussi des tests « bout en bout »).
Généralités
La démarche du TDD
TDD est un acronyme pour « Test Driven Development » ou encore le développement dirigé par les tests en français. Il s'agit
d'une démarche qui recommande au développeur de rédiger l'ensemble des tests unitaires avant de développer un logiciel ou les
portions dont il a la charge.
Cette démarche permet de s'assurer que le développeur ne sera pas influencé par son développement lors de l'écriture des cas
de tests afin que ceux-ci soient davantage exhaustifs et conformes aux spécifications. Ainsi, cette démarche permet aussi de
s'assurer que le développeur maîtrise les spécifications dont il a la charge avant de commencer à coder.
L'approche TDD préconise le cycle court de développement en cinq étapes :
1. écrire les cas de tests d'une fonction ;
2. vérifier que les tests échouent (car le code n'existe pas encore) ;
3. développer le code fonctionnel suffisant pour valider tous les cas de tests ;
4. vérifier que les tests passent ;
5. refactoriser et optimiser en s'assurant que les tests continuent de passer.
Le framework JUnit
Qu’est-ce que JUnit
JUnit est un framework Java prévu pour la réalisation de tests unitaires et d'intégration.
JUnit est le framework le plus connu de la mouvance des frameworks xUnit implémentés dans de nombreuses technologies (nous
pourrons également citer PHPUnit pour PHP ou encore xUnit.NET pour C# et .NET par exemple).
JUnit permet de réaliser :
❖ des TestCase qui sont des classes contenant des méthodes de tests ;
❖ des TestSuite qui permettent de lancer des suites de classes de type TestCase.
Voir la slide suivante pour avoir la structure globale d’une classe de test écrite en JUnit >= 4.
Le framework JUnit
import org.junit.Before;
import org.junit.After;
import org.junit.BeforeClass;
import org.junit.AfterClass;
import org.junit.Test;
public class MaClasseDeTest {
/** Pre et post conditions */
@BeforeClass
public static void
setUpBeforeClass() throws Exception
{
// Le contenu de cette
méthode ne sera exécuté qu'une fois
avant toutes les autres méthodes
avec annotations
// (y compris celles ayant
une annotation @Before)
}
@AfterClass
public static void tearDownClass()
throws Exception {
// Le contenu de cette méthode
ne sera exécuté qu'une fois après
toutes les autres méthodes avec
annotations
// (y compris celles ayant une
annotation @After)
}
@Before
public void setUp() throws
Exception {
// Le contenu de cette méthode
sera exécuté avant chaque test (méthode
avec l'annotation @Test)
}
@After
public void tearDown() throws
Exception {
// Le contenu de cette méthode
sera exécuté après chaque test (méthode
avec l'annotation @Test)
}
/** Cas de tests */
@Test
public void testCas1() {
// Code contenant l'exécution du
premier scénario avec les assertions
associées
}
@Test
public void testCas2() {
// ...
}
}
Le framework JUnit
Les différents types d’assertions avec JUnit
En informatique, une assertion est une expression qui doit être évaluée vrai ou faire échouer le programme ou le test en cas d’
échec (en levant une exception ou en mettant fin au programme par exemple).
Dans le cas de JUnit et des tests d'une manière générale, on peut assimiler une assertion à une vérification et, en cas d’échec, une
exception spécifique est levée (java.lang.AssertionErrorpour JUnit).
Voici une liste non exhaustive des fonctions d'assertions en JUnit :
● assertEquals: vérifier l'égalité de deux expressions ;
● assertNotNull: vérifier la non-nullité d'un objet ;
● assertNull: vérifier la nullité d'un objet ;
● assertTrue: vérifier qu'une expression booléenne est vraie ;
● assertFalse: vérifier qu'une expression booléenne est fausse ;
● fail : échouer le test si cette assertion est exécutée.
Ces méthodes sont surchargées pour de nombreux objets. Par exemple, la méthode assertEquals va être surchargée pour tous les
types primitifs et pour les classes implémentant la méthode equals (String par exemple). Il est également possible de fournir un
message d'erreur en cas d'échec d'une assertion (en premier paramètre).
Le framework JUnit
Exemples d’assertions simples
Imaginons que l’on veuille tester la classe suivante :
public class Addition {
Integer nb1, nb2;
public Addition(Integer nb1, Integer nb2){
this.nb1 = nb1;
this.nb2 = nb2;
}
public Integer somme(){
return nb1 + nb2;
}
public Integer getNb1() {
return nb1;
}
public Integer getNb2() {
return nb2;
}
}
Voici un exemple de fonction de test :
@Test
public void testCasAdditionNominal() {
Addition add = new Addition(1, 2);
// vérification, on a bien valorisé l'attribut nb1
assertEquals(1, add.getNb1());
// vérification, on a bien valorisé l'attribut nb2
assertEquals(2, add.getNb2());
// vérification sur la méthode somme()
assertEquals("Erreur dans le calcul de la somme",
3, add.somme());
}
Remarque : les imports statiques sont préconisés pour les méthodes statiques
provenant des frameworks de tests présentés dans ce BBL.
Le framework JUnit
Exemples d’assertions sur des exceptions
Il est possible de vérifier la levée d'une exception grâce à l'annotation @Test(expected=<nom>.class) :
// Ce test va réussir
@Test(expected = java.lang.NullPointerException.class)
public final void testException(){
Long variablePasInstanciee = null;
variablePasInstanciee.toString();
}
// Ce test va échouer
@Test(expected = java.lang.NullPointerException.class)
public final void testException2(){
Long variableInstanciee = 1L;
variableInstanciee.toString();
}
L’alternative AssertJ
Une alternative aux assertions JUnit sont les assertions d’AssertJ, une librairie mettant à disposition des assertions tout en
adoptant le “fluent code style”. Il s’agit de la syntaxe préconisée sur le projet Hub numérique.
Voici des exemples d’assertions issues de la documentation officielle d’AssertJ:
// unique entry point to get access to all assertThat methods and utility methods (e.g. entry)
import static org.assertj.core.api.Assertions.*;
// basic assertions
assertThat(frodo.getName()).isEqualTo("Frodo");
assertThat(frodo).isNotEqualTo(sauron)
.isIn(fellowshipOfTheRing);
// String specific assertions
assertThat(frodo.getName()).startsWith("Fro")
.endsWith("do")
.isEqualToIgnoringCase("frodo");
// collection specific assertions
assertThat(fellowshipOfTheRing).hasSize(9)
.contains(frodo, sam)
.doesNotContain(sauron);
L’alternative AssertJ
// using extracting magical feature to check fellowshipOfTheRing characters name :)
assertThat(fellowshipOfTheRing).extracting("name")
.contains("Boromir", "Gandalf", "Frodo", "Legolas")
.doesNotContain("Sauron", "Elrond");
// Extracting with Java 8 love (type safe)
assertThat(fellowshipOfTheRing).extracting(TolkienCharacter::getName)
.contains("Boromir", "Gandalf", "Frodo", "Legolas")
.doesNotContain("Sauron", "Elrond");
// filter collection before assertion
assertThat(fellowshipOfTheRing).filteredOn("race", HOBBIT)
.containsOnly(sam, frodo, pippin, merry);
// filter collection with Java 8 Predicate
assertThat(fellowshipOfTheRing).filteredOn(character -> character.getName().contains("o"))
.containsOnly(aragorn, frodo, legolas, boromir);
// combining filtering and extraction (yes we can)
assertThat(fellowshipOfTheRing).filteredOn(character -> character.getName().contains("o"))
.containsOnly(aragorn, frodo, legolas, boromir)
.extracting(character -> character.getRace().getName())
.contains("Hobbit", "Elf", "Man");
Les objets simulacres
Différences entre mock (simulacres) et stubs (bouchons)
Bien que les concepts d'objets simulacres (mocks) et de bouchons (stubs) soient proches, on peut noter les différences suivantes :
Les bouchons (stubs) sont basés sur une vérification d'état. Ils permettent de s'abstraire d'une dépendance en fournissant des méthodes, Web
services, base de données, ayant les mêmes entrants que cette dépendance, mais en fournissant une réponse contrôlée ou prédéfinie.
Ainsi l'utilisation de bouchons permet de :
❖ tester uniquement la méthode que l'on souhaite tester sans dépendre du résultat fourni par la dépendance en question ;
❖ de développer autour de dépendances non implémentées.
Les objets simulacres (mocks), eux, sont basés sur une vérification de comportement. Contrairement aux bouchons, il ne peut s'agir que d'objets,
mais dont le comportement est également totalement contrôlé. Contrairement aux bouchons qui sont appelés de manière transparente pour
l'appelant, l'utilisation de mocks repose sur l'injection de dépendance.
Le gros avantage des mocks par rapport aux simples bouchons : permet d'être mis en place très rapidement à l'aide d'API comme Mockito au sein
même d'un testCase JUnit et permet de faire des assertions sur les appels aux méthodes du mock (vérifier qu'on appelle bien telle méthode avec
tels arguments par exemple).
Les mocks sont donc plus adaptés à l'écriture de tests unitaires ou l'on ne charge que la classe à tester en essayant de passer par tous les chemins
possibles et de faire le maximum d'assertions sur les cas de tests possibles.
Les bouchons, quant à eux, sont davantage adaptés à la mise en place de tests d'intégration où l'on charge une partie de l'environnement des
portions de code testées (par exemple : de vrais appels HTTP à des Web services bouchonnés dans le cadre d'un projet SOA).
Les objets simulacres
Les différents frameworks existants pour faire des Mocks
Voici une liste non exhaustive des frameworks permettant de mettre en place des objets simulacres en Java :
❖ Mockito
❖ EasyMock
❖ JMock
❖ PowerMock
❖ JMockit
A notre que Mockito est sans doute le plus populaire aujourd’hui. A noter également que PowerMock est essentiellement utilisé
en tant que framework complémentaire permettant de mocker des méthodes statiques (ce que ne permet pas les autres
frameworks) et propose aux choix la syntaxe EasyMock ou Mockito.
Le framework Mockito
Créer un mock
import static org.mockito.Mockito.*;
ClasseAMocker objetMock = mock(ClasseAMocker.class)
Prédire le résultat d'une méthode d’un mock
import static org.mockito.Mockito.*;
import static org.mockito.Matchers.*;
when(objetMock.methode(any(ClassArgument.class))).thenReturn(objetResultat);
when(objetMock.methode(eq(valeurArgument))).thenReturn(objetResultat);
when(objetMock.methode(valeurArgument)).thenReturn(objetResultat);
Le framework Mockito
Faire une assertion sur l’invocation d’un mock
import static org.mockito.Mockito.*;
import static org.mockito.Matchers.*;
verify(objetMock).methode(valeurArgument);
verify(objetMock).methode(eq(valeurArgument));
verify(objetMock).methode(any(ClassArgument.class));
verify(objetMock, times(0)).methode(any(ClassArgument.class)); // vérifier que la méthode n’est jamais invoquée
verify(objetMock, times(1)).methode(any(ClassArgument.class)); // vérifier que la méthode n’est invoquée qu’une fois
Les matchers
Voici une liste non exhaustive des matchers utilisés dans les instructions verify ou when :
❖ “eq” pour equals : il est implicite lorsque l’invocation du mock ne fait pas appel à d’autres matchers
❖ “any” et ses dérivés “anyString”, “anyLong”, “anyListOf(...)” : il est préconisé sur ce projet de ne pas les utiliser si vous avez la
connaissance de votre jeu de données.
Pourquoi ? Car il est préférable de maitriser son jeu de données et de ne pas faire des tests unitaires hasardeux.
Le framework Mockito
Utilisation des annotations
Il est possible d'instancier directement les objets de
Mockito tels que les mocks ou les ArgumentCaptor à l'aide
d'annotations en utilisant le runner « MockitoJUnitRunner
» de la façon suivante :
@RunWith(MockitoJUnitRunner.class)
public class MaClasseDeTest{
@Mock
ObjetMock mock;
// Dependancy injection
@InjectMocks
ControllerUsingMock controller;
@Captor
ArgumentCaptor<ArgumentAVerifier> captor;
}
Cela reviens à écrire le code suivant :
ObjetMock mock = mock(ObjetMock.class);
ArgumentCaptor<ArgumentAVerifier> captor = ArgumentCaptor.
forClass(ArgumentAVerifier.class);
ControllerUsingMock controller = new ControllerUsingMock();
controller.setObjectMock(mock);
Le framework Mockito
Les arguments captor
Ils servent à faire des assertions plus approfondies sur les paramètres passés à un objet simulacres lors d’une assertion de type
“verify”. Ces captors sont principalement utiles lorsque nous n’avons pas connaissance à l’avance de la référence de l’objet qui va
être passé en paramètre (exemple : construction d’une liste au sein de la méthode testée qui va invoquer le mock avec la
référence de cette liste). Dans le cas contraire, il est préconisé sur ce projet d’utiliser le matcher “eq” avec la référence de l’
objet.
import org.mockito.ArgumentCaptor;
import static org.mockito.Mockito.*;
import static org.mockito.Matchers.*;
ArgumentCaptor<ObjetParametreAVerifier> captor = ArgumentCaptor.forClass(ObjetParametreAVerifier.class);
verify(ObjetMock).methodeAVerifier(eq(valeur), captor.capture());
ObjetParametreAVerifier objetAVerifier = captor.getValue();
// assertions sur les méthodes de objetAVerifier
Pour créer un captor sur un paramètre de type List<?>, il faut privilégier l'utilisation des annotations :
@Captor
ArgumentCaptor<List<String>> captor;
Le framework Mockito
Les espions (spy)
Cette fois-ci, on s’écarte du principe des simulacres puisqu’il s’agit de faire des assertions sur l’invocation de méthode d’instance
réelles d’objets. En effet, Mockito permet également de faire des “verify” sur des “espions” à l’instar des mocks.
Exemple d’espion sur une liste (exemple tirée de la documentation de Mockito) :
import static org.mockito.Mockito.*;
@Test
public void whenSpyingOnList_thenCorrect() {
List<String> list = new ArrayList<String>();
List<String> spyList = spy(list);
spyList.add("one");
spyList.add("two");
verify(spyList).add("one");
verify(spyList).add("two");
assertEquals(2, spyList.size());
}
Questions
Merci, avez vous des questions ?
Sources
Voici les sources ayant servi à faire ces slides :
❖ FAQ tests en Java sur Developpez.com par Idriss Neumann : http://java.developpez.com/faq/tests/
❖ Documentation officielle AssertJ : http://joel-costigliola.github.io/assertj/
❖ Site officiel Mockito : http://mockito.org/

More Related Content

What's hot

L'API Collector dans tous ses états
L'API Collector dans tous ses étatsL'API Collector dans tous ses états
L'API Collector dans tous ses étatsJosé Paumard
 
Mocking in Java with Mockito
Mocking in Java with MockitoMocking in Java with Mockito
Mocking in Java with MockitoRichard Paul
 
Java 8-streams-collectors-patterns
Java 8-streams-collectors-patternsJava 8-streams-collectors-patterns
Java 8-streams-collectors-patternsJosé Paumard
 
Testing with JUnit 5 and Spring - Spring I/O 2022
Testing with JUnit 5 and Spring - Spring I/O 2022Testing with JUnit 5 and Spring - Spring I/O 2022
Testing with JUnit 5 and Spring - Spring I/O 2022Sam Brannen
 
Introduction to Java 11
Introduction to Java 11 Introduction to Java 11
Introduction to Java 11 Knoldus Inc.
 
An Introduction to JUnit 5 and how to use it with Spring boot tests and Mockito
An Introduction to JUnit 5 and how to use it with Spring boot tests and MockitoAn Introduction to JUnit 5 and how to use it with Spring boot tests and Mockito
An Introduction to JUnit 5 and how to use it with Spring boot tests and Mockitoshaunthomas999
 
TDD (Test Driven Developement) et refactoring
TDD (Test Driven Developement) et refactoringTDD (Test Driven Developement) et refactoring
TDD (Test Driven Developement) et refactoringneuros
 
Effective testing with pytest
Effective testing with pytestEffective testing with pytest
Effective testing with pytestHector Canto
 
JUnit & Mockito, first steps
JUnit & Mockito, first stepsJUnit & Mockito, first steps
JUnit & Mockito, first stepsRenato Primavera
 
Dynamic content generation
Dynamic content generationDynamic content generation
Dynamic content generationEleonora Ciceri
 
Clean Code II - Dependency Injection
Clean Code II - Dependency InjectionClean Code II - Dependency Injection
Clean Code II - Dependency InjectionTheo Jungeblut
 
Introduction to jmeter
Introduction to jmeterIntroduction to jmeter
Introduction to jmetertest test
 
API Asynchrones en Java 8
API Asynchrones en Java 8API Asynchrones en Java 8
API Asynchrones en Java 8José Paumard
 

What's hot (20)

L'API Collector dans tous ses états
L'API Collector dans tous ses étatsL'API Collector dans tous ses états
L'API Collector dans tous ses états
 
Mocking in Java with Mockito
Mocking in Java with MockitoMocking in Java with Mockito
Mocking in Java with Mockito
 
Java 8 Lambda Expressions
Java 8 Lambda ExpressionsJava 8 Lambda Expressions
Java 8 Lambda Expressions
 
Apache jMeter
Apache jMeterApache jMeter
Apache jMeter
 
Clean code
Clean codeClean code
Clean code
 
Java 8-streams-collectors-patterns
Java 8-streams-collectors-patternsJava 8-streams-collectors-patterns
Java 8-streams-collectors-patterns
 
JUnit Presentation
JUnit PresentationJUnit Presentation
JUnit Presentation
 
Spring jdbc
Spring jdbcSpring jdbc
Spring jdbc
 
Testing with JUnit 5 and Spring - Spring I/O 2022
Testing with JUnit 5 and Spring - Spring I/O 2022Testing with JUnit 5 and Spring - Spring I/O 2022
Testing with JUnit 5 and Spring - Spring I/O 2022
 
Introduction to Java 11
Introduction to Java 11 Introduction to Java 11
Introduction to Java 11
 
An Introduction to JUnit 5 and how to use it with Spring boot tests and Mockito
An Introduction to JUnit 5 and how to use it with Spring boot tests and MockitoAn Introduction to JUnit 5 and how to use it with Spring boot tests and Mockito
An Introduction to JUnit 5 and how to use it with Spring boot tests and Mockito
 
TDD (Test Driven Developement) et refactoring
TDD (Test Driven Developement) et refactoringTDD (Test Driven Developement) et refactoring
TDD (Test Driven Developement) et refactoring
 
Using Mockito
Using MockitoUsing Mockito
Using Mockito
 
Effective testing with pytest
Effective testing with pytestEffective testing with pytest
Effective testing with pytest
 
JUnit & Mockito, first steps
JUnit & Mockito, first stepsJUnit & Mockito, first steps
JUnit & Mockito, first steps
 
Dynamic content generation
Dynamic content generationDynamic content generation
Dynamic content generation
 
Clean Code II - Dependency Injection
Clean Code II - Dependency InjectionClean Code II - Dependency Injection
Clean Code II - Dependency Injection
 
Clean Code
Clean CodeClean Code
Clean Code
 
Introduction to jmeter
Introduction to jmeterIntroduction to jmeter
Introduction to jmeter
 
API Asynchrones en Java 8
API Asynchrones en Java 8API Asynchrones en Java 8
API Asynchrones en Java 8
 

Similar to Bbl sur les tests

testUnitaire (1).pptx
testUnitaire (1).pptxtestUnitaire (1).pptx
testUnitaire (1).pptxManalAg
 
TDD avec ou sans mock
TDD avec ou sans mockTDD avec ou sans mock
TDD avec ou sans mockYannick Ameur
 
Les cinq bonnes pratiques des Tests Unitaires dans un projet Agile
Les cinq bonnes pratiques des Tests Unitaires dans un projet AgileLes cinq bonnes pratiques des Tests Unitaires dans un projet Agile
Les cinq bonnes pratiques des Tests Unitaires dans un projet AgileDenis Voituron
 
Test unitaire
Test unitaireTest unitaire
Test unitaireIsenDev
 
Automatisation des tests - objectifs et concepts - partie 2
Automatisation des tests  - objectifs et concepts - partie 2Automatisation des tests  - objectifs et concepts - partie 2
Automatisation des tests - objectifs et concepts - partie 2Christophe Rochefolle
 
Avis d'expert : Les Tests Logiciels
Avis d'expert : Les Tests LogicielsAvis d'expert : Les Tests Logiciels
Avis d'expert : Les Tests LogicielsCloudNetCare
 
[PFE] Master en ingénierie du logiciel
[PFE] Master en ingénierie du logiciel[PFE] Master en ingénierie du logiciel
[PFE] Master en ingénierie du logicielUSTHB & DELTALOG
 
Industrialisation des développements logiciels
Industrialisation des développements logicielsIndustrialisation des développements logiciels
Industrialisation des développements logicielsSylvain Leroy
 
Delphi et les tests unitaires
Delphi et les tests unitairesDelphi et les tests unitaires
Delphi et les tests unitairespprem
 
13-Cours de Géniel Logiciel
13-Cours de Géniel Logiciel13-Cours de Géniel Logiciel
13-Cours de Géniel Logiciellauraty3204
 
Cocoaheads Paris Nombembre Test unitaires
Cocoaheads Paris Nombembre Test unitairesCocoaheads Paris Nombembre Test unitaires
Cocoaheads Paris Nombembre Test unitairesCocoaHeads France
 
Guide tests fonctionnels
Guide tests fonctionnelsGuide tests fonctionnels
Guide tests fonctionnelscvcby
 

Similar to Bbl sur les tests (20)

Test unitaires
Test unitairesTest unitaires
Test unitaires
 
testUnitaire (1).pptx
testUnitaire (1).pptxtestUnitaire (1).pptx
testUnitaire (1).pptx
 
J Unit
J UnitJ Unit
J Unit
 
Anatomie du test
Anatomie du testAnatomie du test
Anatomie du test
 
Test logiciel
Test logicielTest logiciel
Test logiciel
 
TDD avec ou sans mock
TDD avec ou sans mockTDD avec ou sans mock
TDD avec ou sans mock
 
Flex Unit Testing
Flex Unit TestingFlex Unit Testing
Flex Unit Testing
 
Les cinq bonnes pratiques des Tests Unitaires dans un projet Agile
Les cinq bonnes pratiques des Tests Unitaires dans un projet AgileLes cinq bonnes pratiques des Tests Unitaires dans un projet Agile
Les cinq bonnes pratiques des Tests Unitaires dans un projet Agile
 
Test unitaire
Test unitaireTest unitaire
Test unitaire
 
Automatisation des tests - objectifs et concepts - partie 2
Automatisation des tests  - objectifs et concepts - partie 2Automatisation des tests  - objectifs et concepts - partie 2
Automatisation des tests - objectifs et concepts - partie 2
 
Conformiq
ConformiqConformiq
Conformiq
 
Avis d'expert : Les Tests Logiciels
Avis d'expert : Les Tests LogicielsAvis d'expert : Les Tests Logiciels
Avis d'expert : Les Tests Logiciels
 
[PFE] Master en ingénierie du logiciel
[PFE] Master en ingénierie du logiciel[PFE] Master en ingénierie du logiciel
[PFE] Master en ingénierie du logiciel
 
Industrialisation des développements logiciels
Industrialisation des développements logicielsIndustrialisation des développements logiciels
Industrialisation des développements logiciels
 
Delphi et les tests unitaires
Delphi et les tests unitairesDelphi et les tests unitaires
Delphi et les tests unitaires
 
13-Cours de Géniel Logiciel
13-Cours de Géniel Logiciel13-Cours de Géniel Logiciel
13-Cours de Géniel Logiciel
 
Cocoaheads Paris Nombembre Test unitaires
Cocoaheads Paris Nombembre Test unitairesCocoaheads Paris Nombembre Test unitaires
Cocoaheads Paris Nombembre Test unitaires
 
chap6_GL.pptx
chap6_GL.pptxchap6_GL.pptx
chap6_GL.pptx
 
Les tests en PHP
Les tests en PHPLes tests en PHP
Les tests en PHP
 
Guide tests fonctionnels
Guide tests fonctionnelsGuide tests fonctionnels
Guide tests fonctionnels
 

More from Idriss Neumann

Suivre l'évolution du covid19 sur RaspberryPi avec la suite Elastic
Suivre l'évolution du covid19 sur RaspberryPi avec la suite ElasticSuivre l'évolution du covid19 sur RaspberryPi avec la suite Elastic
Suivre l'évolution du covid19 sur RaspberryPi avec la suite ElasticIdriss Neumann
 
Using the restful twitter’s api
Using the restful twitter’s apiUsing the restful twitter’s api
Using the restful twitter’s apiIdriss Neumann
 
Industrialisation des contrats d'interface avec netflix feign
Industrialisation des contrats d'interface avec netflix feignIndustrialisation des contrats d'interface avec netflix feign
Industrialisation des contrats d'interface avec netflix feignIdriss Neumann
 
Bbl microservices avec vert.x cdi elastic search
Bbl microservices avec vert.x cdi elastic searchBbl microservices avec vert.x cdi elastic search
Bbl microservices avec vert.x cdi elastic searchIdriss Neumann
 

More from Idriss Neumann (6)

Suivre l'évolution du covid19 sur RaspberryPi avec la suite Elastic
Suivre l'évolution du covid19 sur RaspberryPi avec la suite ElasticSuivre l'évolution du covid19 sur RaspberryPi avec la suite Elastic
Suivre l'évolution du covid19 sur RaspberryPi avec la suite Elastic
 
Presentation uprodit
Presentation uproditPresentation uprodit
Presentation uprodit
 
Bash bonnes pratiques
Bash bonnes pratiquesBash bonnes pratiques
Bash bonnes pratiques
 
Using the restful twitter’s api
Using the restful twitter’s apiUsing the restful twitter’s api
Using the restful twitter’s api
 
Industrialisation des contrats d'interface avec netflix feign
Industrialisation des contrats d'interface avec netflix feignIndustrialisation des contrats d'interface avec netflix feign
Industrialisation des contrats d'interface avec netflix feign
 
Bbl microservices avec vert.x cdi elastic search
Bbl microservices avec vert.x cdi elastic searchBbl microservices avec vert.x cdi elastic search
Bbl microservices avec vert.x cdi elastic search
 

Bbl sur les tests

  • 1. BBL sur les tests JUnit, Mockito, AssertJ Une petite soif ? Sirotez donc un mockito (sans alcool bien sûr :p) en écoutant CE BBL! Par Idriss Neumann, le 11/02/2016
  • 2. Sommaire ❖ Généralités ➢ Pourquoi écrire des tests ? ➢ Différence entre TI et TU ➢ Démarche du TDD ❖ Le framework JUnit ➢ Définition ➢ Structure d’un test case ➢ Les différentes assertions ➢ Assertions sur exception ❖ L’alternative AssertJ ❖ Les objets simulacres ➢ Différences entre mock et stub ➢ Liste des mocking toolkits ❖ Le framework Mockito ➢ Créer un mock ➢ Prédire le résultat d’une méthode d’un mock ➢ Faire des assertions sur l’invocation d’un mock ➢ L’utilisation des annotations ➢ Les arguments captors ➢ Les espions (spy) ❖ Question
  • 3. Généralités Pourquoi écrire des tests ? Écrire des scénarios de tests automatisés (tant des tests unitaires que des tests d'intégration) constitue une étape incontournable du cycle de développement logiciel dans un cadre professionnel. Bien que cette tâche puisse parfois sembler rébarbative, elle est indispensable pour produire des développements de bonne qualité, notamment parce que cela permet entre autres de : ❖ s'assurer que l'on maîtrise les aspects fonctionnels de l'application (les besoins métier à satisfaire) ; ❖ détecter au plus tôt les anomalies éventuelles avant la livraison d'un projet aux utilisateurs ; ❖ détecter d'éventuelles régressions, suite à l'implémentation de nouvelles fonctionnalités, en automatisant l'exécution de l'ensemble de tous les scénarios de tests implémentés sous forme de tests unitaires ou tests d'intégration, de façon à ce qu'ils soient exécutés régulièrement (à minima une fois par jour par exemple). Plus l'ensemble de vos codes sera couvert par les tests, plus vous serez à même de détecter les régressions, de maîtriser l'ensemble des fonctionnalités de votre application et ainsi de la rendre plus maintenable.
  • 4. Généralités Différence entre tests unitaires et tests d’intégration De manière générale, le test unitaire a pour but de tester une partie précise d'un logiciel (d'où le nom "unitaire") voire une fonction précise, en essayant de couvrir au maximum la combinatoire des cas fonctionnels possibles sur cette portion de code. Cependant, cela ne suffit pas à différencier un test unitaire d'un test d'intégration qui, lui aussi, peut se contenter de tester une portion précise du code. La différence fondamentale est que le test unitaire doit uniquement tester le code et ne doit donc pas avoir d'interactions avec des dépendances externes du module testé telles que des bases de données, des Web services, des appels à des procédures distantes, la lecture ou écriture de fichiers ou encore d'autres fonctions. Les dépendances devront être simulées à l'aide de mécanismes tels que les bouchons ou encore les mocks. Concernant les tests d'intégration, ceux-ci permettent, de manière générale, de vérifier la bonne intégration de la portion de code testée dans l'ensemble du logiciel et de ses dépendances. Il peut s'agir tout aussi bien de vérifier la bonne insertion de données dans un SGBDR sur une petite portion de code que de scénarios complets du logiciel, correspondant à des enchaînements d'appels de fonctions, de Web services... (c'est ce que l'on appelle aussi des tests « bout en bout »).
  • 5. Généralités La démarche du TDD TDD est un acronyme pour « Test Driven Development » ou encore le développement dirigé par les tests en français. Il s'agit d'une démarche qui recommande au développeur de rédiger l'ensemble des tests unitaires avant de développer un logiciel ou les portions dont il a la charge. Cette démarche permet de s'assurer que le développeur ne sera pas influencé par son développement lors de l'écriture des cas de tests afin que ceux-ci soient davantage exhaustifs et conformes aux spécifications. Ainsi, cette démarche permet aussi de s'assurer que le développeur maîtrise les spécifications dont il a la charge avant de commencer à coder. L'approche TDD préconise le cycle court de développement en cinq étapes : 1. écrire les cas de tests d'une fonction ; 2. vérifier que les tests échouent (car le code n'existe pas encore) ; 3. développer le code fonctionnel suffisant pour valider tous les cas de tests ; 4. vérifier que les tests passent ; 5. refactoriser et optimiser en s'assurant que les tests continuent de passer.
  • 6. Le framework JUnit Qu’est-ce que JUnit JUnit est un framework Java prévu pour la réalisation de tests unitaires et d'intégration. JUnit est le framework le plus connu de la mouvance des frameworks xUnit implémentés dans de nombreuses technologies (nous pourrons également citer PHPUnit pour PHP ou encore xUnit.NET pour C# et .NET par exemple). JUnit permet de réaliser : ❖ des TestCase qui sont des classes contenant des méthodes de tests ; ❖ des TestSuite qui permettent de lancer des suites de classes de type TestCase. Voir la slide suivante pour avoir la structure globale d’une classe de test écrite en JUnit >= 4.
  • 7. Le framework JUnit import org.junit.Before; import org.junit.After; import org.junit.BeforeClass; import org.junit.AfterClass; import org.junit.Test; public class MaClasseDeTest { /** Pre et post conditions */ @BeforeClass public static void setUpBeforeClass() throws Exception { // Le contenu de cette méthode ne sera exécuté qu'une fois avant toutes les autres méthodes avec annotations // (y compris celles ayant une annotation @Before) } @AfterClass public static void tearDownClass() throws Exception { // Le contenu de cette méthode ne sera exécuté qu'une fois après toutes les autres méthodes avec annotations // (y compris celles ayant une annotation @After) } @Before public void setUp() throws Exception { // Le contenu de cette méthode sera exécuté avant chaque test (méthode avec l'annotation @Test) } @After public void tearDown() throws Exception { // Le contenu de cette méthode sera exécuté après chaque test (méthode avec l'annotation @Test) } /** Cas de tests */ @Test public void testCas1() { // Code contenant l'exécution du premier scénario avec les assertions associées } @Test public void testCas2() { // ... } }
  • 8. Le framework JUnit Les différents types d’assertions avec JUnit En informatique, une assertion est une expression qui doit être évaluée vrai ou faire échouer le programme ou le test en cas d’ échec (en levant une exception ou en mettant fin au programme par exemple). Dans le cas de JUnit et des tests d'une manière générale, on peut assimiler une assertion à une vérification et, en cas d’échec, une exception spécifique est levée (java.lang.AssertionErrorpour JUnit). Voici une liste non exhaustive des fonctions d'assertions en JUnit : ● assertEquals: vérifier l'égalité de deux expressions ; ● assertNotNull: vérifier la non-nullité d'un objet ; ● assertNull: vérifier la nullité d'un objet ; ● assertTrue: vérifier qu'une expression booléenne est vraie ; ● assertFalse: vérifier qu'une expression booléenne est fausse ; ● fail : échouer le test si cette assertion est exécutée. Ces méthodes sont surchargées pour de nombreux objets. Par exemple, la méthode assertEquals va être surchargée pour tous les types primitifs et pour les classes implémentant la méthode equals (String par exemple). Il est également possible de fournir un message d'erreur en cas d'échec d'une assertion (en premier paramètre).
  • 9. Le framework JUnit Exemples d’assertions simples Imaginons que l’on veuille tester la classe suivante : public class Addition { Integer nb1, nb2; public Addition(Integer nb1, Integer nb2){ this.nb1 = nb1; this.nb2 = nb2; } public Integer somme(){ return nb1 + nb2; } public Integer getNb1() { return nb1; } public Integer getNb2() { return nb2; } } Voici un exemple de fonction de test : @Test public void testCasAdditionNominal() { Addition add = new Addition(1, 2); // vérification, on a bien valorisé l'attribut nb1 assertEquals(1, add.getNb1()); // vérification, on a bien valorisé l'attribut nb2 assertEquals(2, add.getNb2()); // vérification sur la méthode somme() assertEquals("Erreur dans le calcul de la somme", 3, add.somme()); } Remarque : les imports statiques sont préconisés pour les méthodes statiques provenant des frameworks de tests présentés dans ce BBL.
  • 10. Le framework JUnit Exemples d’assertions sur des exceptions Il est possible de vérifier la levée d'une exception grâce à l'annotation @Test(expected=<nom>.class) : // Ce test va réussir @Test(expected = java.lang.NullPointerException.class) public final void testException(){ Long variablePasInstanciee = null; variablePasInstanciee.toString(); } // Ce test va échouer @Test(expected = java.lang.NullPointerException.class) public final void testException2(){ Long variableInstanciee = 1L; variableInstanciee.toString(); }
  • 11. L’alternative AssertJ Une alternative aux assertions JUnit sont les assertions d’AssertJ, une librairie mettant à disposition des assertions tout en adoptant le “fluent code style”. Il s’agit de la syntaxe préconisée sur le projet Hub numérique. Voici des exemples d’assertions issues de la documentation officielle d’AssertJ: // unique entry point to get access to all assertThat methods and utility methods (e.g. entry) import static org.assertj.core.api.Assertions.*; // basic assertions assertThat(frodo.getName()).isEqualTo("Frodo"); assertThat(frodo).isNotEqualTo(sauron) .isIn(fellowshipOfTheRing); // String specific assertions assertThat(frodo.getName()).startsWith("Fro") .endsWith("do") .isEqualToIgnoringCase("frodo"); // collection specific assertions assertThat(fellowshipOfTheRing).hasSize(9) .contains(frodo, sam) .doesNotContain(sauron);
  • 12. L’alternative AssertJ // using extracting magical feature to check fellowshipOfTheRing characters name :) assertThat(fellowshipOfTheRing).extracting("name") .contains("Boromir", "Gandalf", "Frodo", "Legolas") .doesNotContain("Sauron", "Elrond"); // Extracting with Java 8 love (type safe) assertThat(fellowshipOfTheRing).extracting(TolkienCharacter::getName) .contains("Boromir", "Gandalf", "Frodo", "Legolas") .doesNotContain("Sauron", "Elrond"); // filter collection before assertion assertThat(fellowshipOfTheRing).filteredOn("race", HOBBIT) .containsOnly(sam, frodo, pippin, merry); // filter collection with Java 8 Predicate assertThat(fellowshipOfTheRing).filteredOn(character -> character.getName().contains("o")) .containsOnly(aragorn, frodo, legolas, boromir); // combining filtering and extraction (yes we can) assertThat(fellowshipOfTheRing).filteredOn(character -> character.getName().contains("o")) .containsOnly(aragorn, frodo, legolas, boromir) .extracting(character -> character.getRace().getName()) .contains("Hobbit", "Elf", "Man");
  • 13. Les objets simulacres Différences entre mock (simulacres) et stubs (bouchons) Bien que les concepts d'objets simulacres (mocks) et de bouchons (stubs) soient proches, on peut noter les différences suivantes : Les bouchons (stubs) sont basés sur une vérification d'état. Ils permettent de s'abstraire d'une dépendance en fournissant des méthodes, Web services, base de données, ayant les mêmes entrants que cette dépendance, mais en fournissant une réponse contrôlée ou prédéfinie. Ainsi l'utilisation de bouchons permet de : ❖ tester uniquement la méthode que l'on souhaite tester sans dépendre du résultat fourni par la dépendance en question ; ❖ de développer autour de dépendances non implémentées. Les objets simulacres (mocks), eux, sont basés sur une vérification de comportement. Contrairement aux bouchons, il ne peut s'agir que d'objets, mais dont le comportement est également totalement contrôlé. Contrairement aux bouchons qui sont appelés de manière transparente pour l'appelant, l'utilisation de mocks repose sur l'injection de dépendance. Le gros avantage des mocks par rapport aux simples bouchons : permet d'être mis en place très rapidement à l'aide d'API comme Mockito au sein même d'un testCase JUnit et permet de faire des assertions sur les appels aux méthodes du mock (vérifier qu'on appelle bien telle méthode avec tels arguments par exemple). Les mocks sont donc plus adaptés à l'écriture de tests unitaires ou l'on ne charge que la classe à tester en essayant de passer par tous les chemins possibles et de faire le maximum d'assertions sur les cas de tests possibles. Les bouchons, quant à eux, sont davantage adaptés à la mise en place de tests d'intégration où l'on charge une partie de l'environnement des portions de code testées (par exemple : de vrais appels HTTP à des Web services bouchonnés dans le cadre d'un projet SOA).
  • 14. Les objets simulacres Les différents frameworks existants pour faire des Mocks Voici une liste non exhaustive des frameworks permettant de mettre en place des objets simulacres en Java : ❖ Mockito ❖ EasyMock ❖ JMock ❖ PowerMock ❖ JMockit A notre que Mockito est sans doute le plus populaire aujourd’hui. A noter également que PowerMock est essentiellement utilisé en tant que framework complémentaire permettant de mocker des méthodes statiques (ce que ne permet pas les autres frameworks) et propose aux choix la syntaxe EasyMock ou Mockito.
  • 15. Le framework Mockito Créer un mock import static org.mockito.Mockito.*; ClasseAMocker objetMock = mock(ClasseAMocker.class) Prédire le résultat d'une méthode d’un mock import static org.mockito.Mockito.*; import static org.mockito.Matchers.*; when(objetMock.methode(any(ClassArgument.class))).thenReturn(objetResultat); when(objetMock.methode(eq(valeurArgument))).thenReturn(objetResultat); when(objetMock.methode(valeurArgument)).thenReturn(objetResultat);
  • 16. Le framework Mockito Faire une assertion sur l’invocation d’un mock import static org.mockito.Mockito.*; import static org.mockito.Matchers.*; verify(objetMock).methode(valeurArgument); verify(objetMock).methode(eq(valeurArgument)); verify(objetMock).methode(any(ClassArgument.class)); verify(objetMock, times(0)).methode(any(ClassArgument.class)); // vérifier que la méthode n’est jamais invoquée verify(objetMock, times(1)).methode(any(ClassArgument.class)); // vérifier que la méthode n’est invoquée qu’une fois Les matchers Voici une liste non exhaustive des matchers utilisés dans les instructions verify ou when : ❖ “eq” pour equals : il est implicite lorsque l’invocation du mock ne fait pas appel à d’autres matchers ❖ “any” et ses dérivés “anyString”, “anyLong”, “anyListOf(...)” : il est préconisé sur ce projet de ne pas les utiliser si vous avez la connaissance de votre jeu de données. Pourquoi ? Car il est préférable de maitriser son jeu de données et de ne pas faire des tests unitaires hasardeux.
  • 17. Le framework Mockito Utilisation des annotations Il est possible d'instancier directement les objets de Mockito tels que les mocks ou les ArgumentCaptor à l'aide d'annotations en utilisant le runner « MockitoJUnitRunner » de la façon suivante : @RunWith(MockitoJUnitRunner.class) public class MaClasseDeTest{ @Mock ObjetMock mock; // Dependancy injection @InjectMocks ControllerUsingMock controller; @Captor ArgumentCaptor<ArgumentAVerifier> captor; } Cela reviens à écrire le code suivant : ObjetMock mock = mock(ObjetMock.class); ArgumentCaptor<ArgumentAVerifier> captor = ArgumentCaptor. forClass(ArgumentAVerifier.class); ControllerUsingMock controller = new ControllerUsingMock(); controller.setObjectMock(mock);
  • 18. Le framework Mockito Les arguments captor Ils servent à faire des assertions plus approfondies sur les paramètres passés à un objet simulacres lors d’une assertion de type “verify”. Ces captors sont principalement utiles lorsque nous n’avons pas connaissance à l’avance de la référence de l’objet qui va être passé en paramètre (exemple : construction d’une liste au sein de la méthode testée qui va invoquer le mock avec la référence de cette liste). Dans le cas contraire, il est préconisé sur ce projet d’utiliser le matcher “eq” avec la référence de l’ objet. import org.mockito.ArgumentCaptor; import static org.mockito.Mockito.*; import static org.mockito.Matchers.*; ArgumentCaptor<ObjetParametreAVerifier> captor = ArgumentCaptor.forClass(ObjetParametreAVerifier.class); verify(ObjetMock).methodeAVerifier(eq(valeur), captor.capture()); ObjetParametreAVerifier objetAVerifier = captor.getValue(); // assertions sur les méthodes de objetAVerifier Pour créer un captor sur un paramètre de type List<?>, il faut privilégier l'utilisation des annotations : @Captor ArgumentCaptor<List<String>> captor;
  • 19. Le framework Mockito Les espions (spy) Cette fois-ci, on s’écarte du principe des simulacres puisqu’il s’agit de faire des assertions sur l’invocation de méthode d’instance réelles d’objets. En effet, Mockito permet également de faire des “verify” sur des “espions” à l’instar des mocks. Exemple d’espion sur une liste (exemple tirée de la documentation de Mockito) : import static org.mockito.Mockito.*; @Test public void whenSpyingOnList_thenCorrect() { List<String> list = new ArrayList<String>(); List<String> spyList = spy(list); spyList.add("one"); spyList.add("two"); verify(spyList).add("one"); verify(spyList).add("two"); assertEquals(2, spyList.size()); }
  • 20. Questions Merci, avez vous des questions ?
  • 21. Sources Voici les sources ayant servi à faire ces slides : ❖ FAQ tests en Java sur Developpez.com par Idriss Neumann : http://java.developpez.com/faq/tests/ ❖ Documentation officielle AssertJ : http://joel-costigliola.github.io/assertj/ ❖ Site officiel Mockito : http://mockito.org/