Как заставить mockito-объект возвращать что-то другое при следующем вызове?

202

Итак, я создаю фиктивный объект как статическую переменную на уровне класса следующим образом ... В одном тесте я хочу Foo.someMethod()вернуть определенное значение, а в другом тесте я хочу, чтобы он возвращал другое значение. Проблема в том, что мне кажется, что мне нужно перестроить макеты, чтобы заставить это работать правильно. Я хотел бы избежать перестройки макетов и просто использовать одни и те же объекты в каждом тесте.

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.

    }

}

Во втором тесте я все еще получаю 0 в качестве значения при вызове testObj.bar () ... Каков наилучший способ решить эту проблему? Обратите внимание, что я знаю, что мог бы использовать разные макеты Fooв каждом тесте, однако мне нужно было объединить несколько запросов mockFoo, то есть мне пришлось бы создавать цепочки в каждом тесте.

Polaris878
источник

Ответы:

43

Прежде всего, не делайте макет статичным. Сделайте это частным полем. Просто поместите ваш класс setUp в @Beforenot @BeforeClass. Это может быть куча, но это дешево.

Во-вторых, то, как вы это делаете сейчас, является правильным способом заставить насмешку вернуть что-то другое в зависимости от теста.

shoebox639
источник
439

Вы также можете заглушить последовательные звонки (# 10 в 2.8.9 api). В этом случае вы должны использовать несколько вызовов thenReturn или один вызов thenReturn с несколькими параметрами (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
Я думаю, вы также можете воспользоваться тем фактом, что .thenReturn () принимает переменные, поэтому код можно сократить до: when (mockFoo.someMethod ()). ThenReturn (0, 1, -1);
Джастин Мюллер
10
@JustinMuller - я думаю, что это стоит отдельного ответа (в отличие от комментария)
Брайан Агнью
16
Этот ответ не является правильным в этом случае. Если вы заглушите этот метод для возврата 0 и 1, то все будет в порядке, пока вы выполняете, test1а затем test2. Но может случиться так, что ваша среда непрерывной интеграции будет запускать тесты в другом порядке. Или может случиться так, что вы захотите запустить test2сам, без test1первого запуска , и в этом случае произойдет сбой. Модульные тесты всегда должны быть независимы друг от друга; и никогда не должно быть зависимости между отдельными тестами или зависимости от определенного порядка тестов. Принимая во внимание, что цепные thenReturnзаявления ...
Дауд ибн Карим
4
... имеет свои применения, как и использование varargs для одного thenReturn, это не правильное решение в данном конкретном случае. Мне кажется, что полчища первооткрывателей здесь, скорее всего, не поняли вопроса.
Дауд ибн Карим
2
Сам Junit не обеспечивает тестовый заказ без@FixMethodOrder
Роджер
29

Для всех, кто ищет что-то, а затем другое исключение вызова:

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

или

    when(mockFoo.someMethod())
            .thenReturn(obj1, obj2)
            .thenThrow(new RuntimeException("Fail"));
Надя Аттила
источник
19

Или даже чище:

when(mockFoo.someMethod()).thenReturn(obj1, obj2);
Педро Х
источник
2
Это должно быть ответом.
Иктиандер
14

Для тех, кто использует spy () и doReturn () вместо метода when ():

то, что вам нужно, чтобы вернуть другой объект при разных вызовах, это:

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

,

Для классических издевательств:

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

или за исключением того, что его бросили:

when(mockFoo.someMethod())
        .thenReturn(obj1)
        .thenThrow(new IllegalArgumentException())
        .thenReturn(obj2, obj3);
fl0w
источник