Як сказати макетному об'єкту 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, тобто я повинен був би робити ланцюжок у кожному тесті.

Відповіді:


43

Перш за все не робіть макет статичним. Зробіть це приватним полем. Просто покладіть свій клас setUp в @Beforenot @BeforeClass. Це може бути запущена купа, але це дешево.

По-друге, спосіб, який ви зараз маєте, - це правильний спосіб змусити повернути щось інше залежно від тесту.


438

Ви також можете зупинити послідовні дзвінки (№10 в 2.8.9 api). У цьому випадку ви б використовували кілька викликів thenReturn або один callReturn з декількома параметрами (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
Сам Джуніт не забезпечує тестового замовлення без@FixMethodOrder
Роджер

29

Для всіх, хто шукає, щоб щось повернути, а потім виключити інший виклик:

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

або

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


14

Для всіх, хто використовує шпигуна () та 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);
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.