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?


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

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

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?


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)

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();
Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.