mockitoDémarrer avec mockito


Remarques

Mockito est un framework Java Mocking qui permet de créer des tests unitaires lisibles en utilisant son API simple. Il diffère des autres structures moqueuses en laissant le modèle expect-run-verify que la plupart des autres cadres utilisent.

Au lieu de cela, il ne connaît qu'un seul moyen de simuler des classes et des interfaces (non finales) et permet de vérifier et de stub en se basant sur des comparateurs d'arguments flexibles.

La version actuelle 1.10.19 est mieux obtenue en utilisant maven

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

ou gradle

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

La version 2 est encore en version bêta.

Versions

Version Maven Central Notes de version Date de sortie
2.1.0 mockito-noyau changements 2016-10-04
1.10.19 mockito-core changements 2014-12-31

Ajouter un comportement à un objet simulé

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 sur le deuxième appel, vous pouvez ajouter l'argument de retour voulu à 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 méthode sans ajouter de comportement à la simulation, elle retournera null:

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

Dans le cas où la méthode simulée a des paramètres, vous devez également déclarer les 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 lancer une exception, utilisez la méthode thenThrow:

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

mock.returnSomething(); // throws Exception
 

Vérifier les arguments passés à simuler

Supposons que nous ayons cette classe et que nous aimerions tester la méthode doSmth . Dans ce cas, nous voulons voir si le paramètre "val" est passé à foo . L'objet foo est moqué.

public class Bar {

    private final Foo foo;

    public Bar(final Foo foo) {
        this.foo = foo;
    }

    public void doSmth() {
        foo.bla("val");
    }
}
 

Nous pouvons y parvenir avec ArgumentCaptor :

@Mock
private Foo fooMock;

@InjectMocks
private Bar underTest;

@Captor
private ArgumentCaptor<String> stringCaptor;

@Test
public void should_test_smth() {
    underTest.doSmth();

    Mockito.verify(fooMock).bla(stringCaptor.capture());

    assertThat(stringCaptor.getValue(), is("val"));
}
 

Créer des objets moqués par Mockito

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

  • via annotation
  • via la fonction simulée

Par annotation:

Avec un testeur JUnit:

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

    // ...
}
 

Vous pouvez également utiliser JUnit @Rule , qui fournit les mêmes fonctionnalités que le MockitoJUnitRunner , mais qui ne nécessite pas de @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 @Rule des @Rule "par main":

public class FooTest {
    @Mock
    private Bar barMock;

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

    // ...
}
 

Via la fonction simulée:

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 la convertir explicitement vers le bon type générique:

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

    // ...
}
 

Installation et configuration

Installation

La méthode préférée pour installer Mockito est de déclarer une dépendance sur mockito-core avec un système de construction de choix. Au 22 juillet 2016, la dernière version non bêta est la 1.10.19, mais la migration vers la version 2.x est déjà encouragée .

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 d' org.mockito.Mockito . Ainsi, Mockito peut être importé de manière statique dans une classe de cette manière:

import static org.mockito.Mockito.*;
 

Le point d'entrée de la documentation se trouve dans le javadoc de cette classe.

Se moquer de certaines méthodes sur un objet

Juste quelques méthodes d'un objet peuvent être moquées en utilisant spy() de mockito.

Par exemple, imaginez que cette classe de méthode nécessite un service Web pour fonctionner.

public class UserManager {

    List<User> users;        

    public UserManager() {
        user = new LinkedLisk<User>();
    }
    
    public void addUser(User user) {
        if (isValid(user)) {
            user.add(user);
        } else {
            throw new NotValidUserException();
        }
    }
    
    protected boolean isValid(User user) {
        //some online web service to check if user is valid
    }
    
    public int numberOfUsers() {
        return users.size();
    }
}
 

addUser méthode addUser doit être testée pour effectuer un test utile pour UserManager . Cependant, une dépendance est trouvée ici, isValid nécessite un service Web externe qui n'est pas contenu dans notre code. Ensuite, cette dépendance externe doit être neutralisée.

Dans ce cas, si vous vous contentez de moquer isValid vous pourrez tester le reste des méthodes UserManager .

@Test
public void testAddUser() {
    User user = mock(User.class);
    UserManager  manager = spy(new UserManager());
    
    //it forces to manager.isValid to return true
    doReturn(true).when(manager).isValid(anyObject());
    
    manager.addUser(user);
    assertTrue(manager.numberOfUsers(), 1);
} 
 

Vous pouvez facilement vérifier le scénario où l' user n'est pas valide.

@Test(expectedExceptions = NotValidUserException.class)
public void testNotValidAddUser() {
    User user = mock(User.class);
    UserManager  manager = spy(new UserManager());
    
    //it forces to manager.isValid to return false
    doReturn(false).when(manager).isValid(anyObject());
    
    manager.addUser(user);
} 
 

Test de Mockito minimal simple

Cet exemple montre un test Mockito minimal utilisant une ArrayList :

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);
    }
}
 

Test unitaire simple avec Mockito

La classe que nous allons tester est la suivante:

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 casser 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);
    }  
}
 

Méthodes vides

void méthodes void peuvent être écrasées à l'aide de la famille de méthodes doThrow () , doAnswer () , doNothing () et doCallRealMethod () .

Runnable mock = mock(Runnable.class);

doThrow(new UnsupportedOperationException()).when(mock).run();

mock.run(); // throws the UnsupportedOperationException
 

Notez que les méthodes void ne peuvent pas être écrasées en utilisant when(..) car le compilateur n'aime pas les méthodes void en argument.

Utiliser les annotations Mockito

La classe que nous allons tester est la suivante:

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' @Mock annotation @Mock est un moyen pratique de créer différentes instances de simulation 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 va essayer de résoudre l'injection de dépendance dans l'ordre suivant:

  1. Injection basée sur constructeur - les moqueries sont injectées dans le constructeur avec la plupart des arguments (si certains arguments ne peuvent pas être trouvés, alors les 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 Setter - les mocks sont injectés par type. S'il y a plusieurs propriétés du même type, alors les noms de propriété et les noms simulés seront mis en correspondance.
  3. Injection directe sur le terrain - comme pour l'injection basée sur le setter.

Notez qu'aucune défaillance n'est signalée si l'une des stratégies susmentionnées a échoué.

Veuillez consulter les derniers @InjectMocks pour plus d'informations sur ce mécanisme dans la dernière version de Mockito.

Vérification des appels de méthode sur un objet simulé

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

Mockito.verify(someMock).bla();
 

Dans cet exemple, nous affirmons que la méthode bla été appelée sur l'objet 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 VerificationMode supplémentaire pour verify :

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

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

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

Ce sont plus d'exemples pour le paramètre VerificationMode , fournissant 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
 

Vérification des arguments avec ArgumentCaptor

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

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 transmis à 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 Matcher<T> avec une classe abstraite ArgumentMatcher<T> pour vérifier les arguments. Il utilise une approche différente du 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();
    }

});        
 

A partir des JavaDocs de ArgumentMatcher:

Avertissement: Soyez raisonnable en utilisant des correspondances d'argument compliquées, en particulier des comparateurs d'arguments personnalisés, car cela peut rendre le test moins lisible. Parfois, il est préférable d'implémenter equals () pour les arguments qui sont transmis à mocks (Mockito utilise naturellement des équations () pour la correspondance d'arguments). Cela peut rendre le test plus propre.