Mockito: методы заглушки, возвращающие тип с ограниченными подстановочными знаками

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(). Есть какие-нибудь указатели на то, что делать с методами заглушки, возвращающими тип с ограниченными подстановочными знаками?

Шихар Мишра
источник
Можете ли вы обновить свой фрагмент кода, чтобы показать общие типы?
Millhouse
1
Готово. Мне пришлось удалить теги pre и code, они были удалены <? расширяет Number> из объявления типа.
Шихар Мишра

Ответы:

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раз, заметьте, что это не безопасный тип. Если вы беспокоитесь о безопасности типов, ответ Millhouse будет правильным.

дополнительные детали

Чтобы было ясно, вот наблюдаемая ошибка компилятора,

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не-подстановочный тип, который необходимо захватить. Из текущего стуббинга Mockito ,

OngoingStubbing<T> thenAnswer(Answer<?> answer);
OngoingStubbing<T> thenReturn(T value);
Джон Маккарти
источник
это также частично помогает мне ... но что произойдет, если список, который вы ожидаете вернуть, не пуст?
ttati
вместо пустого списка вы также можете: List <Number> someList = new 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;
}
Millhouse
источник
17

Я ударил то же самое вчера. Оба ответа от @ nondescript1 и @millhouse помогли мне найти обходной путь. Я довольно много использовал тот же код , как @millhouse, за исключением того, что я сделал это немного более общий характер , потому что моя ошибка не была вызвана 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 не предоставляет что-то подобное ....... если не делает?
Vacao
15
В Java 8 его можно сократить: Mockito.when(dummyClass.dummyMethod()).thenAnswer(x -> someList)таким образом, нет необходимости в служебном методе
fikovnik
1
@fikovnik Какое великое открытие "thenAnswer"!
Borjab
6

Я превращаю комментарий фиковника в ответ, чтобы сделать его более заметным, поскольку считаю, что это наиболее элегантное решение с использованием Java 8+.

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

Вместо этого для обхода ошибки компилятора, описанной в этом вопросе, when()можно использовать рекомендованный подход Mockito с thenAnswer()лямбда- выражением (вместо вспомогательного метода):

Mockito.when(mockedClass.mockedMethod()).thenAnswer(x -> resultList)
anothernode
источник
хотя это не дает никаких ошибок времени компиляции, возвращенный список пуст, даже когда мы передаем список с записями.
Венкатеш Колла - user2742897
0

Хотя служебный метод, предложенный Мареком Радонским, работает, есть также другой вариант, который даже не требует (ИМХО странно выглядящего) лямбда-выражения, предложенного фиковником:

Как показывает этот ответ на аналогичный вопрос, вы также можете использовать следующее:

BDDMockito.willReturn(someList).given(dummyClass).dummyMethod();
Андреас Сигел
источник