Utilisation de Mockito avec plusieurs appels à la même méthode avec les mêmes arguments

289

Existe-t-il un moyen pour qu'une méthode tronquée renvoie différents objets lors des invocations suivantes? Je voudrais faire cela pour tester les réponses non déterminées d'un ExecutorCompletionService. c'est-à-dire pour tester que quel que soit l'ordre de retour des méthodes, le résultat reste constant.

Le code que je cherche à tester ressemble à ceci.

// Create an completion service so we can group these tasks together
ExecutorCompletionService<T> completionService =
        new ExecutorCompletionService<T>(service);

// Add all these tasks to the completion service
for (Callable<T> t : ts)
    completionService.submit(request);

// As an when each call finished, add it to the response set.
for (int i = 0; i < calls.size(); i ++) {
    try {
        T t = completionService.take().get();
        // do some stuff that I want to test
    } catch (...) { }        
}
Emma
la source

Réponses:

254

Vous pouvez le faire en utilisant la thenAnswerméthode (lors de l'enchaînement avec when):

when(someMock.someMethod()).thenAnswer(new Answer() {
    private int count = 0;

    public Object answer(InvocationOnMock invocation) {
        if (count++ == 1)
            return 1;

        return 2;
    }
});

Ou en utilisant la doAnswerméthode statique équivalente :

doAnswer(new Answer() {
    private int count = 0;

    public Object answer(InvocationOnMock invocation) {
        if (count++ == 1)
            return 1;

        return 2;
    }
}).when(someMock).someMethod();
Igor Nikolaev
la source
634

Que diriez-vous

when( method-call ).thenReturn( value1, value2, value3 );

Vous pouvez mettre autant d'arguments que vous le souhaitez entre les crochets de thenReturn, à condition qu'ils soient tous du type correct. La première valeur sera renvoyée la première fois que la méthode est appelée, puis la deuxième réponse, etc. La dernière valeur sera renvoyée de manière répétée une fois que toutes les autres valeurs auront été utilisées.

Dawood ibn Kareem
la source
4
Cela fonctionnera avec une maquette, mais pas avec un espion. Si vous devez empêcher d'appeler la méthode d'origine, vous avez besoin de doAnswer (...). When (someSpy) .someMethod (...).
Yuri
6
@Yuri - pas tout à fait. Vous n'avez pas besoin doAnswerou d'écrire un Answerdans le cas que vous mentionnez. Vous pouvez simplement utiliser doReturn(...).when(someSpy).someMethod(...). Il semble raisonnable de supposer qu'Emma s'intéresse aux moqueries, plutôt qu'aux espions, mais je suppose que je pourrais ajouter quelque chose à ma réponse pour l'expliquer. Merci pour le commentaire.
Dawood ibn Kareem
@DawoodibnKareem permet de dire pour le premier appel que je veux retourner une valeur et pour le deuxième appel je veux lever une exception. Comment cela peut-il être fait?
Rito
@Rito Veuillez lire la réponse de Volodymyr ou la réponse de Raystorm. Ils couvrent tous les deux cette affaire.
Dawood ibn Kareem
Une telle réponse glorieuse.
wild_nothing
151

Comme indiqué précédemment presque tous les appels sont chaînables.

Vous pouvez donc appeler

when(mock.method()).thenReturn(foo).thenReturn(bar).thenThrow(new Exception("test"));

//OR if you're mocking a void method and/or using spy instead of mock

doReturn(foo).doReturn(bar).doThrow(new Exception("Test").when(mock).method();

Plus d'informations dans Mockito's Documenation .

Raystorm
la source
3
Très utile! Que se passerait-il la 4ème fois a mock.methodété appelée dans cet exemple? Je veux quelque chose comme, retourner foo la première fois mais retourner la barre pour TOUS les autres.
javaPlease42
6
Chaque invocation supplémentaire sur la maquette retournera le dernier 'thenReturn' ou le dernier 'thenThrow' Très utile
Francois Lacoursiere
Merci pour les grandes et simples instructions. Je n'ai jamais su cela jusqu'à présent. J'avais du mal à trouver comment récupérer deux résultats différents sur deux appels identiques. Économisez-moi des tonnes de temps.
CharlesC
68

Vous pouvez même enchaîner des doReturn()invocations de méthode comme celle-ci

doReturn(null).doReturn(anotherInstance).when(mock).method();

mignon n'est-ce pas :)

Volodymyr Kozubal
la source
4

J'ai implémenté une MultipleAnswerclasse qui m'aide à bloquer différentes réponses à chaque appel. Voici le morceau de code:

private final class MultipleAnswer<T> implements Answer<T> {

    private final ArrayList<Answer<T>> mAnswers;

    MultipleAnswer(Answer<T>... answer) {
        mAnswers = new ArrayList<>();
        mAnswers.addAll(Arrays.asList(answer));
    }

    @Override
    public T answer(InvocationOnMock invocation) throws Throwable {
        return mAnswers.remove(0).answer(invocation);
    }
}
victorvmp
la source
1
Pouvez-vous initialiser cet objet d'une manière courte, simple et lisible?
usr-local-ΕΨΗΕΛΩΝ
1

Les éléments suivants peuvent être utilisés comme méthode courante pour renvoyer différents arguments sur différents appels de méthode. La seule chose que nous devons faire est de passer un tableau avec ordre dans lequel les objets doivent être récupérés à chaque appel.

@SafeVarargs
public static <Mock> Answer<Mock> getAnswerForSubsequentCalls(final Mock... mockArr) {
    return new Answer<Mock>() {
       private int count=0, size=mockArr.length;
       public Mock answer(InvocationOnMock invocation) throws throwable {
           Mock mock = null;
           for(; count<size && mock==null; count++){
                mock = mockArr[count];
           }

           return mock;    
       } 
    }
}

Ex. getAnswerForSubsequentCalls(mock1, mock3, mock2);renverra l'objet mock1 au premier appel, l'objet mock3 au deuxième appel et l'objet mock2 au troisième appel. Doit être utilisé comme when(something()).doAnswer(getAnswerForSubsequentCalls(mock1, mock3, mock2)); ceci est presque similaire àwhen(something()).thenReturn(mock1, mock3, mock2);

yuva 443
la source
1

En relation avec la réponse de @ [Igor Nikolaev] d'il y a 8 ans, l'utilisation d'un Answerpeut être quelque peu simplifiée en utilisant une expression lambda disponible dans Java 8.

when(someMock.someMethod()).thenAnswer(invocation -> {
    doStuff();
    return;
});

ou plus simplement:

when(someMock.someMethod()).thenAnswer(invocation -> doStuff());
MorganGalpin
la source
1

Style BDD:

import static org.mockito.BDDMockito.*;
...
    @Test
    void submit() {
        given(yourMock.yourMethod()).willReturn(1, 2, 3);
epox
la source
1

doReturn (valeur1, valeur2, valeur3) .when (appel de méthode)

EnhancedJack
la source