Jak powiedzieć mockito próbnemu obiektowi, aby zwrócił coś innego przy następnym wywołaniu?

202

Tak więc tworzę próbny obiekt jako zmienną statyczną na poziomie klasy, tak jak ... W jednym teście chcę Foo.someMethod()zwrócić określoną wartość, podczas gdy w innym teście chcę, aby zwróciła inną wartość. Problem, który mam, polega na tym, że wydaje się, że muszę odbudować symulacje, aby działało poprawnie. Chciałbym uniknąć przebudowy prób i po prostu używać tych samych obiektów w każdym teście.

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.

    }

}

W drugim teście nadal otrzymuję 0 jako wartość, gdy wywoływana jest metoda testObj.bar () ... Jaki jest najlepszy sposób na rozwiązanie tego problemu? Zauważ, że wiem, że mógłbym użyć innej makiety Foow każdym teście, jednak muszę łączyć wiele żądań mockFoo, co oznacza, że ​​będę musiał wykonać łańcuch w każdym teście.

Polaris878
źródło

Odpowiedzi:

43

Przede wszystkim nie sprawiają, że makiety są statyczne. Zrób to prywatne pole. Po prostu umieść klasę setUp w @Beforenie @BeforeClass. Może to być kilka, ale jest tanie.

Po drugie, sposób, w jaki masz go teraz, jest prawidłowym sposobem, aby uzyskać próbę zwrócenia czegoś innego w zależności od testu.

shoebox639
źródło
438

Możesz także zablokować kolejne połączenia (# 10 w 2.8.9 API). W takim przypadku użyłbyś wielu połączeń thenReturn lub jednego callReturn z wieloma parametrami (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
    }
}
Tony R.
źródło
171
Myślę, że możesz również skorzystać z faktu, że .thenReturn () przyjmuje varargs, więc kod można skrócić do: when (mockFoo.someMethod ()). ThenReturn (0, 1, -1);
Justin Muller
10
@JustinMuller - myślę, że warto osobną odpowiedź (w przeciwieństwie do komentarza)
Brian Agnew
16
Ta odpowiedź nie jest właściwa w tym przypadku. Jeśli skrótową metoda zwraca 0 i 1, wtedy wszystko będzie w porządku tak długo, jak uruchomić test1i potem test2. Być może jednak środowisko ciągłej integracji przeprowadzi testy w innej kolejności. Lub może być tak, że będziesz chciał uruchomić test2się sam, bez uruchamiania test1najpierw, w takim przypadku zawiedzie. Testy jednostkowe muszą zawsze być od siebie niezależne; i nigdy nie powinna istnieć zależność między poszczególnymi testami ani zależność od określonej kolejności testów. Podczas gdy łańcuchowe thenReturnoświadczenia ...
Dawood ibn Kareem
4
... ma swoje zastosowania, podobnie jak użycie varargs dla jednego thenReturn, nie jest to właściwe rozwiązanie w tym konkretnym przypadku. Wydaje mi się, że hordy upvotersów najprawdopodobniej nie zrozumiały pytania.
Dawood ibn Kareem
2
Sam Junit nie zapewnia zamówienia testowego bez@FixMethodOrder
Roger
29

Dla wszystkich, którzy szukają zwrotu, a następnie innego wyjątku rzucania połączeń:

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

lub

    when(mockFoo.someMethod())
            .thenReturn(obj1, obj2)
            .thenThrow(new RuntimeException("Fail"));
Nagy Attila
źródło
19

Lub nawet czystsze:

when(mockFoo.someMethod()).thenReturn(obj1, obj2);
Pedro H.
źródło
2
To powinna być odpowiedź.
Ikthiander
14

Dla każdego używającego spy () i doReturn () zamiast metody when ():

to, czego potrzebujesz, aby zwrócić inny obiekt przy różnych wywołaniach, to:

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

.

W przypadku klasycznych makiet:

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

lub z wyjątkiem zgłoszonego wyjątku:

when(mockFoo.someMethod())
        .thenReturn(obj1)
        .thenThrow(new IllegalArgumentException())
        .thenReturn(obj2, obj3);
fl0w
źródło