web-dev-qa-db-fra.com

Mockito: utiliser une méthode dans "thenReturn" pour retourner une maquette ne fonctionne pas

J'ai rencontré ce que je suppose être un bogue avec Mockito, mais je me demandais si quelqu'un d'autre pouvait expliquer pourquoi ce test ne fonctionnait pas.

Fondamentalement, j'ai deux objets, comme celui-ci:

public class FirstObject {
    private SecondObject secondObject;
    public SecondObject getSecondObject() { return secondObject; }
}

public class SecondObject {
    private String name;
    public String getName() { return name; }
}

Le premier objet est moqué via l'annotation et la méthode before:

@Mock
FirstObject mockedFirstObject;

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

Le deuxième objet est moqué dans une méthode:

public SecondObject setupMockedSecondObject() {
    SecondObject secondObject = Mockito.mock(SecondObject.class);
    Mockito.when(secondObject.getName()).thenReturn("MockObject");
    return secondObject;
}

Lorsque thenReturn contient un appel direct à cette méthode pour configurer et obtenir une maquette du deuxième objet, il échoue:

@Test
public void notWorkingTest() {
    Mockito.when(mockedFirstObject.getSecondObject()).thenReturn(setupMockedSecondObject());
    Assert.assertEquals(mockedFirstObject.getSecondObject().getName(), "MockObject");
}

Mais, lorsque la maquette retournée par la même méthode est affectée à une variable locale, qui est utilisée dans thenReturn, cela fonctionne:

@Test
public void workingTest() {
    SecondObject mockedSecondObject = setupMockedSecondObject();
    Mockito.when(mockedFirstObject.getSecondObject()).thenReturn(mockedSecondObject);
    Assert.assertEquals(mockedFirstObject.getSecondObject().getName(), "MockObject");
}

Sommes-nous en train de faire quelque chose de mal ou est-ce vraiment un bug/limitation dans Mockito? Y a-t-il une raison délibérée pour que cela ne fonctionne pas?

30
LawrenceWeetman

C'est en effet une limitation de Mockito, et elle est référencée dans leur FAQ :

Puis-je thenReturn() une mock() en ligne?

Malheureusement, vous ne pouvez pas faire cela:

when(m.foo()).thenReturn(mock(Foo.class));
//                         ^

La raison en est que la détection du stubbing inachevé ne fonctionnerait pas si nous autorisons la construction ci-dessus. Nous considérons que c'est un "compromis" de la validation du framework (voir aussi précédent FAQ entry). Cependant vous pouvez légèrement changer le code pour le faire fonctionner:

//extract local variable and start smiling:
Foo foo = mock(Foo.class);
when(m.foo()).thenReturn(foo);

La solution de contournement, comme mentionné, consiste à stocker la valeur renvoyée souhaitée dans une variable locale, comme vous l'avez fait.

D'après ce que je comprends, Mockito valide l'utilisation que vous en faites à chaque fois que vous appelez ses méthodes. Lorsqu'une autre méthode est appelée pendant un processus de stubbing en cours, vous interrompez son processus de validation.

37
Tunaki