Débuter avec mockito

Ajouter un comportement à l’objet moqué

Mockito.when(mock.returnSomething()).thenReturn("my val");

mock.returnSomething(); // returns "my val"
mock.returnSomething(); // returns "my val" again
mock.returnSomething(); // returns "my val" again and again and again...

Si vous voulez une valeur différente lors du deuxième appel, vous pouvez ajouter l’argument de retour souhaité à la méthode thenReturn :

Mockito.when(mock.returnSomething()).thenReturn("my val", "other val");

mock.returnSomething(); // returns "my val"
mock.returnSomething(); // returns "other val"
mock.returnSomething(); // returns "other val" again

Si vous appelez la méthode sans ajouter de comportement pour se moquer, elle renverra null :

barMock.mock.returnSomethingElse(); // returns null

Dans le cas où cette méthode simulée a des paramètres, vous devez également déclarer des valeurs :

Mockito.when(mock.returnSomething("param 1")).thenReturn("my val 1");
Mockito.when(mock.returnSomething("param 2")).thenReturn("my val 2");

mock.returnSomething("param 1"); // returns "my val 1"
mock.returnSomething("param 2"); // returns "my val 2"
mock.returnSomething("param 3"); // returns null

Si vous ne vous souciez pas de la valeur du paramètre, vous pouvez utiliser Matchers.any() :

Mockito.when(mock.returnSomething(Matchers.any())).thenReturn("p1");

mock.returnSomething("param 1"); // returns "p1"
mock.returnSomething("param other"); // returns "p1"

Pour lever une exception, utilisez la méthode thenThrow :

Mockito.when(mock.returnSomething()).thenThrow(new Exception());

mock.returnSomething(); // throws Exception

Créer des objets moqués par Mockito

Il existe deux manières de créer un objet simulé par Mockito :

  • par annotation
  • via la fonction de simulation

Par annotation :

Avec un exécuteur de test JUnit :

@RunWith(MockitoJUnitRunner.class)
public class FooTest {
    @Mock
    private Bar barMock;

    // ...
}

Vous pouvez également utiliser JUnit @Rule de Mockito, qui fournit les mêmes fonctionnalités que MockitoJUnitRunner, mais n’a pas besoin d’un lanceur de test @RunWith :

public class FooTest {
    @Rule
    public MockitoRule mockito = MockitoJUnit.rule();        

    @Mock
    private Bar barMock;

    // ...
}

Si vous ne pouvez pas utiliser @RunWith ou l’annotation @Rule, vous pouvez également init mocks “per hand”:

public class FooTest {
    @Mock
    private Bar barMock;

    @Before
    public void setUp() {
        MockitoAnnotations.initMocks(this);
    }

    // ...
}

** Via la fonction de simulation : **

public class FooTest {
    private Bar barMock = Mockito.mock(Bar.class);

    // ...
}

En raison de l’effacement de type, vous ne pouvez pas vous moquer d’une classe générique comme ci-dessus. Vous devez simuler la classe de base et transtyper explicitement vers le bon type générique :

public class FooTest {
    private Bar<String> genericBarMock = (Bar<String>) Mockito.mock(Bar.class);

    // ...
}

Vérifier les appels de méthode sur l’objet simulé

Pour vérifier si une méthode a été appelée sur un objet simulé, vous pouvez utiliser la méthode Mockito.verify :

Mockito.verify(someMock).bla();

Dans cet exemple, nous affirmons que la méthode bla a été appelée sur l’objet factice someMock.

Vous pouvez également vérifier si une méthode a été appelée avec certains paramètres :

Mockito.verify(someMock).bla("param 1");

Si vous souhaitez vérifier qu’une méthode n’a pas été appelée, vous pouvez passer un paramètre supplémentaire VerificationMode à verify :

Mockito.verify(someMock, Mockito.times(0)).bla();

Cela fonctionne également si vous souhaitez vérifier que cette méthode a été appelée plus d’une fois (dans ce cas, nous vérifions que la méthode bla a été appelée 23 fois) :

Mockito.verify(someMock, Mockito.times(23)).bla();

Voici d’autres exemples pour le paramètre VerificationMode, offrant plus de contrôle sur le nombre de fois qu’une méthode doit être appelée :

Mockito.verify(someMock, Mockito.never()).bla(); // same as Mockito.times(0)

Mockito.verify(someMock, Mockito.atLeast(3)).bla(); // min 3 calls

Mockito.verify(someMock, Mockito.atLeastOnce()).bla(); // same as Mockito.atLeast(1)

Mockito.verify(someMock, Mockito.atMost(3)).bla(); // max 3 calls

Test unitaire simple avec Mockito

La classe que nous allons tester est :

public class Service {

    private Collaborator collaborator;

    public Service(Collaborator collaborator) {
        this.collaborator = collaborator;
    }
    
    public String performService(String input) {
        return collaborator.transformString(input);
    }
}

Son collaborateur est :

public class Collaborator {

    public String transformString(String input) {
        return doStuff();
    }

    private String doStuff() {
        // This method may be full of bugs
        . . .
        return someString;
    }

}

Dans notre test, nous voulons briser la dépendance de Collaborator et de ses bogues, nous allons donc nous moquer de Collaborator :

import static org.junit.Assert.*;
import static org.mockito.Mockito.*;

import org.junit.Test;

public class ServiceTest {
    @Test
    public void testPerformService() throws Exception {
        // Configure mock
        Collaborator collaboratorMock = mock(Collaborator.class);
        doReturn("output").when(collaboratorMock).transformString("input");

        // Perform the test
        Service service = new Service(collaboratorMock);
        String actual = service.performService("input");
        
        // Junit asserts
        String expected = "output";
        assertEquals(expected, actual);
    }  
}

Utilisation des annotations Mockito

La classe que nous allons tester est :

public class Service{

    private Collaborator collaborator;

    public Service(Collaborator collaborator){
        this.collaborator = collaborator;
    }
    
    
    public String performService(String input){
        return collaborator.transformString(input);
    }
}

Son collaborateur est :

public class Collaborator {

    public String transformString(String input){
        return doStuff();
    }

    private String doStuff()
    {
        // This method may be full of bugs
        . . .
        return someString;
    }

}

Dans notre test, nous voulons briser la dépendance de Collaborator et de ses bogues, nous allons donc nous moquer de Collaborator. L’annotation @Mock est un moyen pratique de créer différentes instances de simulations pour chaque test :

import static org.junit.Assert.*;
import static org.mockito.Mockito.*;

import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Mock;
import org.mockito.InjectMocks;
import org.mockito.runners.MockitoJUnitRunner;

@RunWith(MockitoJUnitRunner.class)
public class ServiceTest {

    @Mock
    private Collaborator collaboratorMock;

    @InjectMocks
    private Service service;
    
    @Test
    public void testPerformService() throws Exception {
        // Configure mock
        doReturn("output").when(collaboratorMock).transformString("input");            

        // Perform the test
        String actual = service.performService("input");
        
        // Junit asserts
        String expected = "output";
        assertEquals(expected, actual);
    }
    
    
    @Test(expected=Exception.class)
    public void testPerformServiceShouldFail() throws Exception {
        // Configure mock
        doThrow(new Exception()).when(collaboratorMock).transformString("input");

        // Perform the test
        service.performService("input");
    }
}

Mockito essaiera de résoudre l’injection de dépendance dans l’ordre suivant :

  1. Injection basée sur le constructeur - les simulacres sont injectés dans le constructeur avec la plupart des arguments (si certains arguments sont introuvables, des valeurs nulles sont transmises). Si un objet a été créé avec succès via le constructeur, aucune autre stratégie ne sera appliquée.
  2. Injection basée sur le setter - les simulacres sont injectés par type. S’il existe plusieurs propriétés du même type, les noms de propriété et les faux noms seront mis en correspondance.
  3. Injection directe sur le terrain - identique à l’injection basée sur le poseur.

Notez qu’aucun échec n’est signalé en cas d’échec de l’une des stratégies susmentionnées.

Veuillez consulter le dernier [@InjectMocks][1] pour des informations plus détaillées sur ce mécanisme dans la dernière version de Mockito.

[1] : http://site.mockito.org/mockito/docs/current/org/mockito/InjectMocks.html

Installation et configuration

Installation

La meilleure façon d’installer Mockito est de déclarer une dépendance sur mockito-core avec un système de construction de choix. Depuis le 22 juillet 2016, la dernière version non bêta est la 1.10.19, mais [2.x est déjà encouragé à migrer vers][1].

Maven

<dependency>
    <groupId>org.mockito</groupId>
    <artifactId>mockito-core</artifactId>
    <version>1.10.19</version>
    <scope>test</scope>
</dependency>

Gradle

repositories { jcenter() }
dependencies { testCompile "org.mockito:mockito-core:1.+" }

Il y a aussi “mockito-all” qui contient Hamcrest et Objenesis en plus de Mockito lui-même. Il est livré via Maven principalement pour les utilisateurs de fourmis, mais la distribution a été interrompue dans Mockito 2.x.


Importer

La plupart des fonctionnalités de Mockito sont des méthodes statiques de org.mockito.Mockito. Ainsi, Mockito peut être importé statiquement dans une classe de cette manière :

import static org.mockito.Mockito.*;

Le point d’entrée de la documentation est situé dans le [javadoc][2] de cette classe.

[1] : http://site.mockito.org/mockito/docs/current/org/mockito/Mockito.html#0 [2] : http://site.mockito.org/mockito/docs/current/org/mockito/Mockito.html

Se moquer de certaines méthodes sur un objet

Test Mockito minimal simple

Cet exemple montre un test Mockito minimal utilisant une ArrayList simulée :

import static org.mockito.Mockito.*;
import static org.junit.Assert.*;

import java.util.ArrayList;

import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Mock;
import org.mockito.runners.MockitoJUnitRunner;

@RunWith(MockitoJUnitRunner.class)
public class MockitoTest
{
    @Mock
    ArrayList<String> listMock;

    @Test
    public void testAppend() {
        // configure the mock to return "foobar" whenever "get()" 
        // is called on "listMock" with an int value as parameter
        doReturn("foobar").when(listMock).get(anyInt());            
        String result = listMock.get(0);
        
        assertEquals("foobar", result);
    }
}

Vérification des arguments avec ArgumentCaptor

Pour valider les arguments des méthodes appelées sur un mock, utilisez la classe ArgumentCaptor. Cela vous permettra d’extraire les arguments dans votre méthode de test et d’effectuer des assertions sur eux.

Cet exemple teste une méthode qui met à jour le nom d’un utilisateur avec un ID donné. La méthode charge l’utilisateur, met à jour l’attribut name avec la valeur donnée et l’enregistre ensuite. Le test veut vérifier que l’argument passé à la méthode save est un objet User avec l’ID et le nom corrects.

// This is mocked in the test
interface UserDao {
    void save(User user);
}

@RunWith(MockitoJUnitRunner.class)
public class UserServiceTest {
    @Mock
    UserDao userDao;

    @Test
    public void testSetNameForUser() {
        UserService serviceUnderTest = new UserService(userDao);
        
        serviceUnderTest.setNameForUser(1L, "John");

        ArgumentCaptor<User> userArgumentCaptor = ArgumentCaptor.forClass(User.class);
        
        verify(userDao).save(userArgumentCaptor.capture());
        User savedUser = userArgumentCaptor.getValue();
        assertTrue(savedUser.getId() == 1);
        assertTrue(savedUser.getName().equals("John"));
    }
}

Vérification des arguments avec ArgumentMatcher

Mockito fournit une interface Matcher<T> avec une classe abstraite ArgumentMatcher<T> pour vérifier les arguments. Il utilise une approche différente pour le même cas d’utilisation que le ArgumentCaptor. De plus, ArgumentMatcher peut également être utilisé pour se moquer. Les deux cas d’utilisation utilisent la méthode Mockito.argThat() qui fournit un code de test raisonnablement lisible.

verify(someMock).someMethod(Mockito.argThat(new ArgumentMatcher<String>() {
       
    @Override
    public boolean matches(Object o) {
        return o instanceof String && !((String)o).isEmpty();
    }

});        

À partir des JavaDocs d’ArgumentMatcher :

Avertissement : Soyez raisonnable lorsque vous utilisez une correspondance d’arguments compliquée, en particulier des correspondances d’arguments personnalisées, car cela peut rendre le test moins lisible. Parfois, il est préférable d’implémenter equals() pour les arguments passés aux mocks (Mockito utilise naturellement equals() pour la correspondance des arguments). Cela peut rendre le test plus propre.

## Méthodes void stubbing

Vérifier les arguments passés à mock