Wie kann man einem Mockito-Scheinobjekt sagen, dass es beim nächsten Aufruf etwas anderes zurückgeben soll?

202

Also erstelle ich ein Scheinobjekt als statische Variable auf Klassenebene wie folgt ... In einem Test möchte ich Foo.someMethod()einen bestimmten Wert zurückgeben, während ich in einem anderen Test möchte, dass es einen anderen Wert zurückgibt. Das Problem, das ich habe, ist, dass ich anscheinend die Mocks neu erstellen muss, damit dies richtig funktioniert. Ich möchte vermeiden, die Mocks neu zu erstellen, und in jedem Test dieselben Objekte verwenden.

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.

    }

}

Im zweiten Test erhalte ich immer noch 0 als Wert, wenn testObj.bar () aufgerufen wird ... Wie kann dies am besten behoben werden? Beachten Sie, dass ich weiß, dass ich Fooin jedem Test ein anderes Modell verwenden kann. Ich muss jedoch mehrere Anforderungen mockFooverketten, was bedeutet, dass ich die Verkettung in jedem Test durchführen muss.

Polaris878
quelle

Antworten:

43

Machen Sie den Schein zunächst nicht statisch. Machen Sie es zu einem privaten Feld. Setzen Sie einfach Ihre setUp-Klasse in das @Beforenicht @BeforeClass. Es könnte ein Haufen laufen, aber es ist billig.

Zweitens ist die Art und Weise, wie Sie es jetzt haben, die richtige Art und Weise, einen Mock dazu zu bringen, je nach Test etwas anderes zurückzugeben.

Schuhkarton639
quelle
438

Sie können auch aufeinanderfolgende Anrufe stubben (# 10 in 2.8.9 api). In diesem Fall würden Sie mehrere thenReturn- Aufrufe oder einen thenReturn- Aufruf mit mehreren Parametern (varargs) verwenden.

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.
quelle
171
Ich denke, Sie können auch die Tatsache ausnutzen, dass .thenReturn () varargs verwendet, sodass der Code auf Folgendes gekürzt werden kann: when (mockFoo.someMethod ()). ThenReturn (0, 1, -1);
Justin Muller
10
@ JustinMüller - das ist eine separate Antwort wert, denke ich (im Gegensatz zu einem Kommentar)
Brian Agnew
16
Diese Antwort ist in diesem Fall nicht richtig. Wenn Sie diese Methode stubben, um 0 und 1 zurückzugeben, ist alles in Ordnung, solange Sie ausführen test1und dann test2. Es kann jedoch sein, dass Ihre Umgebung für die kontinuierliche Integration die Tests in der anderen Reihenfolge ausführt. Oder es kann sein, dass Sie test2selbst ausführen möchten , ohne test1zuerst ausgeführt zu werden. In diesem Fall schlägt dies fehl. Unit Tests müssen immer unabhängig voneinander sein; und es sollte niemals eine Abhängigkeit zwischen einzelnen Tests oder eine Abhängigkeit von einer bestimmten Reihenfolge von Tests geben. Während Verkettung thenReturnAussagen ...
Dawood ibn Kareem
4
... hat seine Verwendung, ebenso wie die Verwendung von varargs für eine einzelne thenReturn, es ist in diesem speziellen Fall keine richtige Lösung. Es scheint mir, dass die Horden von Aufsteigern hier die Frage höchstwahrscheinlich nicht verstanden haben.
Dawood ibn Kareem
2
Junit selbst garantiert keine Testreihenfolge ohne@FixMethodOrder
Roger
29

Für alle, die suchen, um etwas zurückzugeben, und dann für eine andere Call-Throw-Ausnahme:

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

oder

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

Oder noch sauberer:

when(mockFoo.someMethod()).thenReturn(obj1, obj2);
Pedro H.
quelle
2
Dies sollte die Antwort sein.
Ikthiander
14

Für alle, die spy () und doReturn () anstelle der when () -Methode verwenden:

Was Sie benötigen, um bei verschiedenen Aufrufen unterschiedliche Objekte zurückzugeben, ist Folgendes:

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

.

Für klassische Mocks:

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

oder mit einer Ausnahme, die geworfen wird:

when(mockFoo.someMethod())
        .thenReturn(obj1)
        .thenThrow(new IllegalArgumentException())
        .thenReturn(obj2, obj3);
fl0w
quelle