Introduction à Mockito

Transcription

Introduction à Mockito
Introduction à Mockito
Construction de doublures en Java
Contenu
1. Introduction..........................................................................................................................1
2. Création de doublure...........................................................................................................3
3. Stubbing.................................................................................................................................3
4. Vérification............................................................................................................................5
5. Mockito pour Android........................................................................................................8
6. Téléchargement et Installation sous Eclipse.....................................................................8
1. Introduction
Un test unitaire teste une classe en isolation. Les effets de bord des autres classes ou du
système doivent être éliminés si possible pendant le test. L'atteinte de ce but est
généralement rendue difficile par le fait que les classes d'un projet dépendent d'autres
classes.
L'exemple classique est celui du test d'une classe qui fait appel à une base de données.
L'utilisation d'une vraie base de données rend l'écriture des tests très complexe et leur
exécution très longue. Certains objets sont très difficiles à instancier, il peut être même
impossible de le faire si on est en phase de spécification et que seule l'interface a été écrite.
C'est le cas par exemple quand la base de données n'est pas encore disponible. De plus, on ne
peut pas savoir si les problèmes sont dus à la classe en cours de test ou aux classes en
interaction.
C'est là qu'interviennent les doublures. Une doublure est un objet qui simule le
comportement d'une classe et garantie que les conditions du test sont toujours les mêmes.
Ces objets doublures sont fournis à la classe à tester. Ainsi, la classe à tester peut être isolée
des autres classes.
Mockito est un générateur automatique de doublures qui peut être utilisé en conjonction
avec JUnit. Mockito permet de créer des classes "marionnettes" à partir de n'importe quelle
classe ou d'interface. En anglais on appelle ça des "mock objects".
Cette classe nous allons pouvoir en contrôler finement le comportement, comme les
valeurs de retour de ses méthodes : c'est qu'on appelle le "stubbing".
Les objets mocks peuvent être passés à d'autres objets qui sont en test. Les tests peuvent
alors valider que la classe réagit correctement durant les tests. Cela permet de s'assurer que
l'on teste seulement la classe et qu'il n'y a pas d'effets de bord dûs à d'autres classes.
1.1 Cycle de vie d'un mock dans un cas de test
Mockito fonctionne sur le mode de l'espion :
1. Création des mocks ;
2. Description du comportement qu'ils sont censés imiter ;
É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 ■ Introduction à Mockito
3. Utilisation des mocks dans le code qui testent le comportement donné ;
4. Interrogation des mocks pour savoir comment ils ont été utilisés durant le test.
Dans la suite, nous présentons d'abord un exemple récapitulatif puis détaillons chacune
des étapes de la création de doublure.
1.2 Exemple complet
Prenons l'exemple de l'interface List de l'API Java que l'on veut doubler pour tester une
classe qui manipule cette liste sans se préoccuper de gérer effectivement cette liste.
1.2.1 Création de doublure
List mockedList = mock(List.class);
Dans cet exemple, nous venons de créer un objet mock à partir de l'interface List. Cet
objet implémente l'interface. Par contre son comportement est pour le moment assez limité,
un appel à mockedList.get(0) retournera null.
La classe à tester se nomme MaClasse et possède un attribut qui est du type List. On
suppose que le constructeur permet de prendre le type de liste effectif comme paramètre
(sinon on ne peut remplacer la liste en attribut).
ENSICAEN - Spécialité Informatique
MaClasse c = new MaClasse(mockledList);
Pour contrôler le comportement de la doublure que nous avons créée en fonction des
appels que l'on veut simuler, nous allons faire du "stubbing".
1.2.2 Le "stubbing"
La stubbling consiste à définir le comportement souhaité pour chaque appel des
méthodes de l'objet doublé. Supposons que pour tester la classe MaClasse nous appelons la
méthode get(0) de la doublure et que la réponse doit « toto ». Dans cas, il faut définir le
comportement de cet appel particulier :
when(mockedList.get(0)).thenReturn("toto");
Maintenant un appel à mockedList.get(0) retournera la chaîne "toto".
Si l'on souhaite répondre "toto" pour n'importe qu'elle indice de la liste :
when(mockedList.get(anyInt())).thenReturn("toto");
Maintenant un appel à mockedList.get(indice) avec n'importe quel entier en indice
retournera la chaîne "toto".
1.2.3 Vérifier les appels
Il peut être intéressant de contrôler qu'une méthode a bien été appelée et avec les bons
arguments en paramètre. La ligne suivante permet de s'assurer que la valeur retournée par
get(888) est bien "toto". À défaut, une exception est levée.
AssertEquals("Problème d'insertion",mockedList.get(888),"tata");
Si l'on souhaite vérifier qu'il y a bien eu un appel à la méthode get avec comme
paramètre 888.
verify(mockedList).get(888);
À défaut, la JVM lève une exception.
Détaillons maintenant chaque étape de construction.
Introduction à Mockito ■ 3
2. Création de doublure
En préambule à la création de d'objetss mock, notons que les importations statiques
permettent d'appeler les membres statiques d'une classe sans passer par la classe. Avec la
déclaration ci-dessous, il est possible d’appeler la méthode mock() sans passer par
mockito.mock().
import static org.mockito.Mockito.*;
Il y a deux manières de créer des doublures, avec la méthode mock() et avec l'annotation
Java @Mock.
2.1 Création d'une doublure avec la méthode mock()
Supposons que l'on crée une double de la classe UneInterface :
UneInterface mockSansNom = mock(UneInterface.class);
La même création avec un nom rend les messages d'erreurs plus clairs :
UneInterface mockAvecNom = mock(UneInterface.class, "ceMock");
2.2 Création d'une doublure avec l'annotation @Mock
@Mock UneInterface ceMock;
2.3 Comportement par défaut
Supposons une doublure de la classe Bidon.
Bidon monMock = mock(Bidon.class, "bidon");
Dès l'exécution de cet appel, l'objet monMock est créé avec des comportements par défaut,
décrit par les assertions suivantes :
assertEquals("bidon", monMock.toString());
assertEquals("type numérique : 0 ", 0, monMock.retourneUnEntier());
assertEquals("type booléen : false", false, monMock.retourneUnBooleen());
assertEquals("type collection : vide",0,
monMock.retourneUneList().size());
3. Stubbing
Littéralement stub signifie un bouchon. C'est un code minimal ne faisant rien (ou pas
grand-chose) mais prêt à être étendu et utile car permettant d'invoquer un logiciel durant
son développement. Le stubbling consiste à remplacer ce comportement par défaut des
méthodes.
3.1 Stubbing avec la méthode when()
Il a deux cas possibles, selon que la méthode possède a un type de retour ou non.
3.1.1 Appel de méthode avec une valeur de retour unique
Le stubbing :
when(monMock.retourneUnEntier()).thenReturn(3);
ENSICAEN - Spécialité Informatique
Le nom du mock est automatiquement ceMock.
4 ■ Introduction à Mockito
et la description avec JUnit :
assertEquals("une première fois 3",3, monMock.retourneUnEntier());
assertEquals("une deuxième fois 3",3, monMock.retourneUnEntier());
Retourne la valeur stubbée autant de fois que nécessaire.
3.1.2 Appel de méthode avec des valeurs de retour consécutives
Le stubbing :
when(monMock.retourneUnEntier()).thenReturn(3, 4, 5);
et la description avec JUnit :
assertEquals("une première fois : 3", 3, monMock.retourneUnEntier());
assertEquals("une deuxième fois : 4", 4, monMock.retourneUnEntier());
assertEquals("une troisième fois : 5", 5, monMock.retourneUnEntier());
when(monMock.retourneUnEntier()).thenReturn(3, 4);
est un raccourci pour :
when(monMock.retourneUnEntier()).thenReturn(3).thenReturn(4);
3.1.3 Apple de méthode avec utilisation de la valeur des paramètres
ENSICAEN - Spécialité Informatique
Soit la méthode de Bidon :
public int retourneUnEntierBis(int i, int j);
On pourra écrire :
when(monMock.retourneUnEntierBis(4, 2)).thenReturn(4);
when(monMock.retourneUnEntierBis(5, 3)).thenReturn(5);
et la description avec JUnit :
assertEquals("param 4 2: 4", 4, monMock.retourneUnEntierBis(4, 2));
assertEquals("param 5 3: 5", 5, monMock.retourneUnEntierBis(5, 3));
3.2 Levée d'exception
Le fonctionnement est le même. On suppose donnée la méthode :
public int retourneUnEntierOuLeveUneExc() throws BidonException;
Le stubbing est :
when(monMock.retourneUnEntierOuLeveUneExc()).thenReturn(3).thenThrow(new
BidonException());
et la description avec Junit est :
assertEquals("1er appel: ->3",3, monMock.retourneUnEntierOuLeveUneExc());
try {
monMock.retourneUnEntierOuLeveUneExc();
fail();
} catch (BidonException e) {
assertTrue("2nd appel -> exception", true);
}
3.3 Appel de méthode sans valeur de retour mais avec levée d'exception
La méthode de type void est :
public void voidEtLeveUneExc() throws BidonException;
Introduction à Mockito ■ 5
On écrira :
doThrow(new BidonException()).when(monMock).voidEtLeveUneExc();
et la description avec JUnit :
try {
monMock.voidEtLeveUneExc();
fail();
} catch (BidonException e) {
assertTrue("levee exception", true) ;
}
3.3.1 Limitations
Mockito possède certaines limitations. Premièrement, il ne peut tester les constructions
suivantes :
► Les classes déclarées final ;
► Les classes anonymes ;
► Les types primitifs.
Deuxièmement, il ne peut pas stubber les méthodes suivantes des objets :
► hashCode()
3.4 Matchers
Souvent, on veut spécifier un appel sans que les valeurs des paramètres aient vraiment
d’importance. On utilise pour cela des Matchers (import org.mockito.Matchers).
when(mockedList.get(anyInt())).thenReturn("element");
verify(mockedList).get(anyInt());
Voici la liste des matchers qui sont très souvent utilisés :
► any(), static <T> T anyObject().
► anyBoolean(), anyDouble(), anyFloat() , anyInt(), anyString() …
► anyList(), anyMap(), anyCollection(), anyCollectionOf(java.lang.Class<T>clazz),
anySet(), <T> java.util.Set<T> anySetOf(java.lang.Class<T> clazz).
Attention ! Si on utilise des matchers, tous les arguments doivent être des matchers. La
ligne suivante produit une erreur de compilation :
verify(mock).someMethod(anyInt(), anyString(), "third argument");
4. Vérification
Mockito garde trace de tous les appels de méthode avec leurs valeurs de paramètre.
Vous pouvez utiliser la méthode verify() sur un objet mock pour vérifier qu'une méthode
est appelée avec certaines valeurs de paramètres.
Ce type de test est quelque fois appelé test de comportement, parce qu'il ne teste pas le
résultat de l'appel, mais vérifie qu'une méthode est appelée avec les bons paramètres.
@Test
public void test1() {
ENSICAEN - Spécialité Informatique
► equals(). Mockito vérifie les valeurs en arguments en utilisant equals().
6 ■ Introduction à Mockito
MyClass test = Mockito.mock(MyClass.class);
// define return value for method getUniqueId()
test.when(test.getUniqueId()).thenReturn(43);
// TODO use mock in test....
// now check if method testing was called with the parameter 12
Mockito.verify(test).testing(Matchers.eq(12));
// was the method called twice?
Mockito.verify(test, Mockito.times(2));
}
4.1 Fonctionnement de la méthode verify()
Elle permet de vérifier :
► quelles méthodes ont été appelées sur un mock ;
► combien de fois ;
► avec quels paramètres ;
► dans que l'ordre.
ENSICAEN - Spécialité Informatique
Si la vérification échoue, il y a levée d'exception et le test échoue.
4.2 Vérification du nombre d'appels
On vérifie que retourneUnBooleen doit avoir été appelée...
► … exactement une fois :
verify(monMock).retourneUnBooleen();
verify(monMock, times(1)).retourneUnBooleen(); // équivalent
au moins / au plus une fois :
verify(monMock, atLeastOnce()).retourneUnBooleen();
verify(monMock, atMost(1)).retourneUnBooleen();
► … jamais :
verify(monMock, never()).retourneUnBooleen();
► Avec des paramètres :
verify(monMock).retourneUnEntierBis(4, 2);
4.3 Vérification de l'ordre des appels
import org.mockito.InOrder;
Pour vérifier que l'appel (4,2) est effectué avant l'appel (5,3) :
InOrder ordre = inOrder(monMock);
ordre.verify(monMock).retourneUnEntierBis(4, 2);
ordre.verify(monMock).retourneUnEntierBis(5, 3);
Marche aussi avec plusieurs mocks :
InOrder ordre = inOrder(mock1, mock2);
ordre.verify(mock1).foo();
ordre.verify(mock2).yo();
Introduction à Mockito ■ 7
4.4 Et aussi ...
Vérification qu'il n'y a pas d'interaction à utiliser à bon escient :
verifyNoMoreInteractions(mock);
verifyZeroInteractions(mock);
La méthode verifyNoMoreInteractions() permet de vérifier que qu'aucune autre
méthode n'est appelée.
4.4.1 Espionner les méthodes classiques
Il est aussi possible d'espionner un objet classique, qui n'est pas un objet mock.
► Obtenir un objet espionné par la méthode spy :
List list = new LinkedList();
List spy = spy(list);
► Appeler des méthodes « normales » sur le spy :
spy.add("one");
spy.add("two");
► Vérifier les appels à la fin :
L'annotation @Spy ou la méthode spy() peuvent être utilisées pour espionner un objet
concret. Chaque appel, sans spécification contraire, est délégué à l'objet.
// Lets mock a LinkedList
List list = new LinkedList();
List spy = spy(list);
//You have to use doReturn() for stubbing
doReturn("foo").when(spy).get(0);
// this would not work
// real method is called so spy.get(0)
// throws IndexOutOfBoundsException (list is still empty)
when(spy.get(0)).thenReturn("foo");
Il y a aussi l'annotation @InjectMocks qui tente de faire une injection de dépendance
basé sur le type. Par exemple :
public class ArticleManagerTest {
@Mock private ArticleCalculator calculator;
@Mock private ArticleDatabase database;
@Spy private UserProvider userProvider = new ConsumerUserProvider();
// creates instance of ArticleManager
// and performs constructor injection on it
@InjectMocks private ArticleManager manager = new ArticleManager();
@Test public void shouldDoSomething() {
MockitoAnnotations.initMocks(this);
verify(database).addListener(any(ArticleListener.class));
}
}
ENSICAEN - Spécialité Informatique
verify(spy).add("one");
verify(spy).add("two");
8 ■ Introduction à Mockito
5. Mockito pour Android
5.1 Créer une application à tester sur Android
Créer une application Android qui permet de déclencher une intention avec certains
paramètres comme dans l'exemple suivant :
public static Intent createQuery(Context context, String query, String
value) {
Intent i = new Intent(context, MyListActivity.class);
i.putExtra("QUERY", query);
i.putExtra("VALUE", value);
return i;
}
5.2 Créer un test
Créer un nouveau projet de test pour l'application et une nouvelle classe de test. Écrire
un test, en utilisant Mockito pour vérifier que l'intention est déclenchée avec les données
supplémentaires correctes.
ENSICAEN - Spécialité Informatique
Pour cela, construire une doublure de l'objet du contexte avec Mockito comme dans
l'exemple suivant :
import org.mockito.Mock;
import org.mockito.Mockito;
import org.mockito.MockitoAnnotations;
import
import
import
import
android.content.Context;
android.content.Intent;
android.os.Bundle;
android.test.AndroidTestCase;
import fr.ensicaen.cours1i2ac1.mockito.intent.MainActivity;
public class MainActivtityTest extends AndroidTestCase {
@Mock
Context context;
@Override
protected void setUp() throws Exception {
MockitoAnnotations.initMocks(this);
}
public void testQuery() throws Exception {
Intent intent = MainActivity.createQuery(context, "query", "value");
assertNotNull(intent);
Bundle extras = intent.getExtras();
assertNotNull(extras);
assertEquals("query", extras.getString("QUERY"));
assertEquals("value", extras.getString("VALUE"));
}
}
6. Téléchargement et Installation sous Eclipse
Le framework Mockito est hébergé à l'adresse : https://code.google.com/p/mockito/
Introduction à Mockito ■ 9
6.1 Installation
1/ extraire les fichiers de l'archive
2/ Ouvrir un projet Eclipse ou créer un nouveau projet Java.
3/ Ouvrir les propriétés du projet
4/ Sélection « Java Build Path » puis « Libraries »
5/ Cliquer sur « Add external Jars »
6/ Sélectionner tous le fichier jar de Mockito and appuyer sur OK.
6.2 Alternatives
Les solutions alternatives à Mockito sont :
► jMock : http://jmock.org/
► EasyMock : http://easymock.org/
ENSICAEN - Spécialité Informatique
10 ■ Introduction à Mockito
Révision
Version
0.1
Date
Commentaires
Basé sur le cours de Mirabelle Nebut, Université de Lille
09 fév 2014 + Lars Vogel
(http://www.vogella.com/tutorials/Mockito/article.html)
ENSICAEN - Spécialité Informatique

Documents pareils