Мокіто: методи заглушки, які повертають тип із обмеженими дикими картками


135

Розглянемо цей код:

public class DummyClass {
    public List<? extends Number> dummyMethod() {
        return new ArrayList<Integer>();
    }
}
public class DummyClassTest {
    public void testMockitoWithGenerics() {
        DummyClass dummyClass = Mockito.mock(DummyClass.class);
        List<? extends Number> someList = new ArrayList<Integer>();
        Mockito.when(dummyClass.dummyMethod()).thenReturn(someList); //Compiler complains about this
    }
}

Компілятор скаржиться на рядок, який намагається перешкодити поведінці dummyMethod(). Будь-які вказівки на те, як можна говорити про способи заглушки, які повертають тип із обмеженими шаблонами?


Чи можете ви оновити фрагмент коду для показу загальних типів?
млин

1
Зроблено. Мені довелося видалити попередні та кодові теги, вони знімали <? розширює номер> з декларації типу.
Шихар Мішра

Відповіді:


190

Ви також можете використовувати для цього безпечний метод doReturn нетипового типу ,

@Test
public void testMockitoWithGenerics()
{
    DummyClass dummyClass = Mockito.mock(DummyClass.class);
    List<? extends Number> someList = new ArrayList<Integer>();

    Mockito.doReturn(someList).when(dummyClass).dummyMethod();

    Assert.assertEquals(someList, dummyClass.dummyMethod());
}

як обговорювалося в групі Google Mockito.

Хоча це простіше, але thenAnswer, знову ж таки, зауважте, що це не безпечно. Якщо вас турбує безпека типу, відповідь млин є правильним.

Додаткові відомості

Щоб було зрозуміло, ось спостережувана помилка компілятора,

The method thenReturn(List<capture#1-of ? extends Number>) in the type OngoingStubbing<List<capture#1-of ? extends Number>> is not applicable for the arguments (List<capture#2-of ? extends Number>)

Я вважаю, що компілятор призначив перший тип підстановки під час whenдзвінка, а потім не може підтвердити, що другий тип підстановки у thenReturnвиклику такий же.

Схоже, thenAnswerце питання не виникає, оскільки він приймає тип підстановки, тоді як thenReturnприймає тип незаміщеної підстановки, який повинен бути захоплений. З Мокіто, що триває ,

OngoingStubbing<T> thenAnswer(Answer<?> answer);
OngoingStubbing<T> thenReturn(T value);

це частково допомагає і мені ... але що станеться, якщо список, який ви очікуєте повернути, не порожній?
ttati

замість порожнього списку ви також можете зробити: Список <Number> someList = новий ArrayList <Integer> (); someList.add (aNumber);
ttati

32

Я припускаю, що ви хочете мати можливість завантажити someListдеякі відомі значення; ось підхід, який використовує Answer<T>разом із шаблонним методом помічника, щоб забезпечити безпеку всього типу:

@Test
public void testMockitoWithGenericsUsingAnswer()
{
    DummyClass dummyClass =  Mockito.mock(DummyClass.class);

    Answer<List<Integer>> answer = setupDummyListAnswer(77, 88, 99);
    Mockito.when(dummyClass.dummyMethod()).thenAnswer(answer);

    ...
}

private <N extends Number> Answer<List<N>> setupDummyListAnswer(N... values) {
    final List<N> someList = new ArrayList<N>();

    someList.addAll(Arrays.asList(values));

    Answer<List<N>> answer = new Answer<List<N>>() {
        public List<N> answer(InvocationOnMock invocation) throws Throwable {
            return someList;
        }   
    };
    return answer;
}

17

Я вчора потрапив у те саме. Обидва відповіді від @ nondescript1 та @millhouse допомогли мені розібратися у вирішенні проблеми. Я майже використовував той самий код, що і @millhouse, за винятком того, що я зробив його трохи більш загальним, тому що моя помилка викликана не a java.util.List, а com.google.common.base.Optional. Тому мій маленький помічник дозволяє використовувати будь-який тип, Tа не лише List<T>:

public static <T> Answer<T> createAnswer(final T value) {
    Answer<T> dummy = new Answer<T>() {
        @Override
        public T answer(InvocationOnMock invocation) throws Throwable {
            return value;
        }
    };
    return dummy;
}

За допомогою цього допоміжного методу ви можете написати:

Mockito.when(dummyClass.dummyMethod()).thenAnswer(createAnswer(someList));

Це складається просто чудово і робить те саме, що і thenReturn(...)метод.

Хтось знає, чи помилка, яку видає компілятор Java, є помилкою компілятора чи код дійсно невірний?


Це здається простим, простим і, наскільки я можу сказати, правильним. Я не впевнений, чому Mockito не забезпечує щось подібне до цього ......., якщо це не відбувається?
вакао

14
У Java 8 його можна скоротити:, Mockito.when(dummyClass.dummyMethod()).thenAnswer(x -> someList)тому немає потреби в корисному методі
fikovnik

1
@fikovnik Яке чудове відкриття "тоді відповідь"!
borjab

5

Я перетворюю коментар fikovnik у відповідь тут, щоб надати йому більшої наочності, оскільки я думаю, що це найелегантніше рішення з використанням Java 8+.

Документація Mockito рекомендує використовувати doReturn()(як пропонується у прийнятій відповіді) лише в крайньому випадку.

Натомість, щоб обійти помилку компілятора, описану у питанні, рекомендований when()підхід Mockito може бути використаний із thenAnswer()і лямбда (замість хелперного методу):

Mockito.when(mockedClass.mockedMethod()).thenAnswer(x -> resultList)

хоча він не видає помилок часу компіляції, повернений список порожній, навіть коли ми передаємо список із записами.
Venkatesh Kolla - користувач2742897

0

Хоча корисний метод, запропонований Мареком Радонським, працює, є й інший варіант, який навіть не вимагає (IMHO дивно виглядає) лямбда-виразного фіковнику, запропонованого:

Як показує ця відповідь на подібне запитання, ви також можете використовувати наступне:

BDDMockito.willReturn(someList).given(dummyClass).dummyMethod();
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.