Mockito: doAnswer Vs thenReturn

125

J'utilise Mockito pour les tests unitaires ultérieurs. Je suis confus quand utiliser doAnswervs thenReturn.

Quelqu'un peut-il m'aider en détail? Jusqu'à présent, je l'ai essayé avec thenReturn.

Rajkumar Thambu
la source

Réponses:

167

Vous devez utiliser thenReturnou doReturnlorsque vous connaissez la valeur de retour au moment où vous vous moquez d'un appel de méthode. Cette valeur définie est renvoyée lorsque vous appelez la méthode simulée.

thenReturn(T value) Définit une valeur de retour à renvoyer lorsque la méthode est appelée.

@Test
public void test_return() throws Exception {
    Dummy dummy = mock(Dummy.class);
    int returnValue = 5;

    // choose your preferred way
    when(dummy.stringLength("dummy")).thenReturn(returnValue);
    doReturn(returnValue).when(dummy).stringLength("dummy");
}

Answer est utilisé lorsque vous devez effectuer des actions supplémentaires lorsqu'une méthode simulée est invoquée, par exemple lorsque vous devez calculer la valeur de retour basée sur les paramètres de cet appel de méthode.

À utiliser doAnswer()lorsque vous souhaitez stuber une méthode void avec generic Answer.

Réponse spécifie une action qui est exécutée et une valeur de retour qui est renvoyée lorsque vous interagissez avec la maquette.

@Test
public void test_answer() throws Exception {
    Dummy dummy = mock(Dummy.class);
    Answer<Integer> answer = new Answer<Integer>() {
        public Integer answer(InvocationOnMock invocation) throws Throwable {
            String string = invocation.getArgumentAt(0, String.class);
            return string.length() * 2;
        }
    };

    // choose your preferred way
    when(dummy.stringLength("dummy")).thenAnswer(answer);
    doAnswer(answer).when(dummy).stringLength("dummy");
}
Roland Weisleder
la source
salut @Roland Weisleder mais parfois vous devriez retourner un code interne généré par la valeur et rien à voir avec des arguments, par exemple code = UUID.randomUUID(), j'ai trouvé impossible de l'implémenter avec mockito.
zhuguowei
3
Lorsque votre maquette devrait renvoyer un nouvel UUID pour chaque appel, vous implémenteriez le Answerjuste avec return UUID.randomUUID();.
Roland Weisleder le
Puis-je prendre cette méthode à partir de la nouvelle initialisation de la réponse et la mettre dans une méthode, pour rendre le code un peu plus propre?
Ligne
3
@Line Answerest une interface fonctionnelle, donc avec Java 8, vous pouvez la remplacer par une expression lambda. Si le n'est pas assez propre, tout autre refactoring habituel et inhabituel est possible.
Roland Weisleder
@ zhuguowei: retourne du code interne généré par la valeur? Que veux-tu dire par là?
Saurabh Patil
35

doAnsweret thenReturnfaites la même chose si:

  1. Vous utilisez Mock, pas Spy
  2. La méthode que vous stubbing renvoie une valeur, pas une méthode void.

Moquons-nous de ce BookService

public interface BookService {
    String getAuthor();
    void queryBookTitle(BookServiceCallback callback);
}

Vous pouvez stub getAuthor () en utilisant doAnsweret thenReturn.

BookService service = mock(BookService.class);
when(service.getAuthor()).thenReturn("Joshua");
// or..
doAnswer(new Answer() {
    @Override
    public Object answer(InvocationOnMock invocation) throws Throwable {
        return "Joshua";
    }
}).when(service).getAuthor();

Notez que lors de l'utilisation doAnswer, vous ne pouvez pas transmettre une méthode when.

// Will throw UnfinishedStubbingException
doAnswer(invocation -> "Joshua").when(service.getAuthor());

Alors, quand utiliseriez-vous à la doAnswerplace de thenReturn? Je peux penser à deux cas d'utilisation:

  1. Lorsque vous souhaitez "stub" la méthode void.

En utilisant doAnswer, vous pouvez effectuer des actions supplémentaires lors de l'appel de méthode. Par exemple, déclenchez un rappel sur queryBookTitle.

BookServiceCallback callback = new BookServiceCallback() {
    @Override
    public void onSuccess(String bookTitle) {
        assertEquals("Effective Java", bookTitle);
    }
};
doAnswer(new Answer() {
    @Override
    public Object answer(InvocationOnMock invocation) throws Throwable {
        BookServiceCallback callback = (BookServiceCallback) invocation.getArguments()[0];
        callback.onSuccess("Effective Java");
        // return null because queryBookTitle is void
        return null;
    }
}).when(service).queryBookTitle(callback);
service.queryBookTitle(callback);
  1. Lorsque vous utilisez Spy au lieu de Mock

Lorsque vous utilisez when-thenReturn on Spy Mockito appellera la méthode réelle, puis stub votre réponse. Cela peut poser un problème si vous ne souhaitez pas appeler de méthode réelle, comme dans cet exemple:

List list = new LinkedList();
List spy = spy(list);
// Will throw java.lang.IndexOutOfBoundsException: Index: 0, Size: 0
when(spy.get(0)).thenReturn("java");
assertEquals("java", spy.get(0));

En utilisant doAnswer, nous pouvons le stuber en toute sécurité.

List list = new LinkedList();
List spy = spy(list);
doAnswer(invocation -> "java").when(spy).get(0);
assertEquals("java", spy.get(0));

En fait, si vous ne souhaitez pas effectuer d'actions supplémentaires lors de l'appel de méthode, vous pouvez simplement utiliser doReturn.

List list = new LinkedList();
List spy = spy(list);
doReturn("java").when(spy).get(0);
assertEquals("java", spy.get(0));
Aldok
la source
Et si la méthode fictive est nulle?
Igor Donin
1
Igor, c'est exactement là que doAnswer () entre en scène et il l'a couvert dans la réponse ci-dessus.
Saurabh Patil
Lors de l'utilisation, doAnswer(new Answer() { ... return null;}j'obtiens un avertissement dans eclipse pour "La réponse est un type brut. Les références au type générique Answer <T> doivent être paramétrées". Existe-t-il un moyen de résoudre ce problème (sauf en ignorant l'avertissement ofc)?
LazR