Mockito: metody usuwania, które zwracają typ z ograniczonymi symbolami wieloznacznymi

135

Rozważ ten kod:

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
    }
}

Kompilator skarży się na wiersz, który próbuje odgiąć zachowanie dummyMethod(). Jakieś wskazówki, jak obchodzić się z metodami stubbingowymi, które zwracają typ z ograniczonymi symbolami wieloznacznymi?

Shikhar Mishra
źródło
Czy możesz zaktualizować fragment kodu, aby wyświetlić typy ogólne?
Millhouse
1
Gotowe. Musiałem usunąć tagi pre i code, były one usuwane <? rozszerza Number> z deklaracji typu.
Shikhar Mishra

Odpowiedzi:

190

W tym celu możesz również skorzystać z bezpiecznej metody doReturn bez typu ,

@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());
}

jak omówiono na grupie Google Mockito.

Chociaż jest to prostsze niż thenAnswer, ponownie pamiętaj, że nie jest to bezpieczne. Jeśli obawiasz się o bezpieczeństwo typów, odpowiedź Millhouse jest prawidłowa.

Dodatkowe Szczegóły

Dla jasności, oto zaobserwowany błąd kompilatora,

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>)

Uważam, że kompilator przypisał pierwszy typ symbolu wieloznacznego podczas whenwywołania, a następnie nie może potwierdzić, że drugi typ symbolu wieloznacznego w thenReturnwywołaniu jest taki sam.

Wygląda na to, thenAnswerże nie występuje ten problem, ponieważ akceptuje typ wieloznaczny, a przyjmuje typ thenReturninny niż wieloznaczny, który musi zostać przechwycony. Z OngoingStubbing Mockito ,

OngoingStubbing<T> thenAnswer(Answer<?> answer);
OngoingStubbing<T> thenReturn(T value);
John McCarthy
źródło
to też częściowo pomaga ... ale co się stanie, jeśli lista, którą spodziewasz się zwrócić, nie jest pusta?
ttati
zamiast pustej listy możesz także zrobić: List <Number> someList = new ArrayList <Integer> (); someList.add (aNumber);
ttati
32

Zakładam, że chcesz móc załadować someListniektóre znane wartości; oto podejście, które wykorzystuje Answer<T>razem z metodą pomocniczą opartą na szablonach, aby zapewnić bezpieczeństwo wszystkich typów:

@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
źródło
17

Wczoraj uderzyłem w to samo. Obie odpowiedzi udzielone przez @ nondescript1 i @millhouse pomogły mi znaleźć obejście. Użyłem prawie tego samego kodu co @millhouse, z tą różnicą, że uczyniłem go nieco bardziej ogólnym, ponieważ mój błąd nie był spowodowany przez a java.util.List, ale com.google.common.base.Optional. Moja metoda małego pomocnika pozwala więc na każdy typ, Ta nie tylko 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;
}

Dzięki tej metodzie pomocniczej możesz napisać:

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

Kompiluje się dobrze i robi to samo, co thenReturn(...)metoda.

Czy ktoś wie, czy błąd, który generuje kompilator Javy, to błąd kompilatora, czy też kod jest naprawdę nieprawidłowy?

Marek Radonsky
źródło
Wydaje się to proste, proste i, o ile wiem, poprawne. Nie jestem pewien, dlaczego Mockito nie zapewnia czegoś podobnego ....... chyba że tak?
vacao
14
W Javie 8 można to skrócić Mockito.when(dummyClass.dummyMethod()).thenAnswer(x -> someList)
:,
1
@fikovnik Co za wspaniałe odkrycie "thenAnswer"!
borjab
5

Ja obracam fikovnik „s komentarz na odpowiedź tutaj, aby nadać mu bardziej widoczne, jak myślę, że jest to najbardziej eleganckie rozwiązanie z użyciem Java 8+.

Dokumentacja Mockito zaleca używanie doReturn()(jak sugeruje zaakceptowana odpowiedź) tylko w ostateczności.

Zamiast tego, aby ominąć błąd kompilatora opisany w pytaniu, zalecane when()podejście Mockito może być użyte z thenAnswer()i lambdą (zamiast metody pomocniczej):

Mockito.when(mockedClass.mockedMethod()).thenAnswer(x -> resultList)
inny węzeł
źródło
chociaż nie daje żadnych błędów czasu kompilacji, zwracana lista jest pusta, nawet jeśli przekazujemy listę z wpisami.
Venkatesh Kolla - user2742897
0

Chociaż metoda użyteczności zaproponowana przez Marka Radonsky'ego działa, istnieje również inna opcja, która nawet nie wymaga (dziwnie wyglądającego) wyrażenia lambda sugerowanego przez fikovnika:

Jak pokazuje ta odpowiedź na podobne pytanie, możesz również użyć następującego:

BDDMockito.willReturn(someList).given(dummyClass).dummyMethod();
Andreas Siegel
źródło