Come dire a un oggetto simulato Mockito di restituire qualcosa di diverso la prossima volta che viene chiamato?


202

Quindi, sto creando un oggetto simulato come una variabile statica a livello di classe in questo modo ... In un test, voglio Foo.someMethod()restituire un certo valore, mentre in un altro test, voglio che restituisca un valore diverso. Il problema che sto riscontrando è che sembra che abbia bisogno di ricostruire le beffe per farlo funzionare correttamente. Vorrei evitare di ricostruire le beffe e usare gli stessi oggetti in ogni test.

class TestClass {

    private static Foo mockFoo;

    @BeforeClass
    public static void setUp() {
        mockFoo = mock(Foo.class);
    }

    @Test
    public void test1() {
        when(mockFoo.someMethod()).thenReturn(0);

        TestObject testObj = new TestObject(mockFoo);

        testObj.bar(); // calls mockFoo.someMethod(), receiving 0 as the value

    }

    @Test
    public void test2() {
        when(mockFoo.someMethod()).thenReturn(1);

        TestObject testObj = new TestObject(mockFoo);

        testObj.bar(); // calls mockFoo.someMethod(), STILL receiving 0 as the value, instead of expected 1.

    }

}

Nel secondo test, sto ancora ricevendo 0 come valore quando viene chiamato testObj.bar () ... Qual è il modo migliore per risolverlo? Nota che so che potrei usare una derisione diversa Fooin ogni test, tuttavia, devo concatenare più richieste mockFoo, il che significa che dovrei fare il concatenamento in ogni test.

Risposte:


43

Prima di tutto non rendere statica la simulazione. Trasformalo in un campo privato. Inserisci la tua classe setUp nel @Beforeno@BeforeClass . Potrebbe essere eseguito un sacco, ma è economico.

In secondo luogo, il modo in cui lo hai in questo momento è il modo corretto di ottenere un falso per restituire qualcosa di diverso a seconda del test.


439

Puoi anche stubare le chiamate consecutive (n. 10 in 2.8.9 api). In questo caso, useresti più chiamate poi Restituisci o una chiamata poi Restituisci con più parametri (varargs).

import static org.junit.Assert.assertEquals;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.when;

import org.junit.Before;
import org.junit.Test;

public class TestClass {

    private Foo mockFoo;

    @Before
    public void setup() {
        setupFoo();
    }

    @Test
    public void testFoo() {
        TestObject testObj = new TestObject(mockFoo);

        assertEquals(0, testObj.bar());
        assertEquals(1, testObj.bar());
        assertEquals(-1, testObj.bar());
        assertEquals(-1, testObj.bar());
    }

    private void setupFoo() {
        mockFoo = mock(Foo.class);

        when(mockFoo.someMethod())
            .thenReturn(0)
            .thenReturn(1)
            .thenReturn(-1); //any subsequent call will return -1

        // Or a bit shorter with varargs:
        when(mockFoo.someMethod())
            .thenReturn(0, 1, -1); //any subsequent call will return -1
    }
}

171
Penso che puoi anche trarre vantaggio dal fatto che .thenReturn () accetta varargs, quindi il codice può essere abbreviato in: when (mockFoo.someMethod ()). ThenReturn (0, 1, -1);
Justin Muller,

10
@JustinMuller - vale la pena una risposta separata, penso (al contrario di un commento)
Brian Agnew,

16
Questa risposta non è la cosa corretta da fare in questo caso. Se si interrompe questo metodo per restituire 0 e 1, allora andrà tutto bene finché si esegue test1e quindi test2. Ma è possibile che l'ambiente di integrazione continua eseguirà i test nell'altro ordine. Oppure potresti voler correre test2da solo, senza test1prima eseguirlo , nel qual caso fallirà. I test unitari devono essere sempre indipendenti l'uno dall'altro; e non dovrebbe mai esserci una dipendenza tra i singoli test o una dipendenza da un particolare ordinamento dei test. Considerando che concatenare thenReturndichiarazioni ...
Dawood ibn Kareem,

4
... ha i suoi usi, così come usare varargs per un singolo thenReturn, non è una soluzione corretta in questo caso particolare. Mi sembra che le orde di elettori qui molto probabilmente non abbiano capito la domanda.
Dawood ibn Kareem,

2
Junit stesso non garantisce l'ordine di prova senza@FixMethodOrder
Roger,

29

Per tutti coloro che cercano di restituire qualcosa e quindi un'altra eccezione per il lancio di una chiamata:

    when(mockFoo.someMethod())
            .thenReturn(obj1)
            .thenReturn(obj2)
            .thenThrow(new RuntimeException("Fail"));

o

    when(mockFoo.someMethod())
            .thenReturn(obj1, obj2)
            .thenThrow(new RuntimeException("Fail"));

19

O anche più pulito:

when(mockFoo.someMethod()).thenReturn(obj1, obj2);

2
Questa dovrebbe essere la risposta.
Ikthiander

14

Per chiunque usi spy () e il doReturn () invece del metodo when ():

ciò di cui hai bisogno per restituire oggetti diversi su chiamate diverse è questo:

doReturn(obj1).doReturn(obj2).when(this.spyFoo).someMethod();

.

Per beffe classiche:

when(this.mockFoo.someMethod()).thenReturn(obj1, obj2);

o con un'eccezione generata:

when(mockFoo.someMethod())
        .thenReturn(obj1)
        .thenThrow(new IllegalArgumentException())
        .thenReturn(obj2, obj3);
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.