Używanie Mockito do kpienia z klas za pomocą ogólnych parametrów


280

Czy istnieje czysta metoda wyśmiewania klasy za pomocą ogólnych parametrów? Powiedz, że muszę wyśmiewać klasę, Foo<T>którą muszę przekazać do metody, która oczekuje Foo<Bar>. Z łatwością mogę wykonać następujące czynności:

Foo mockFoo = mock(Foo.class);
when(mockFoo.getValue).thenReturn(new Bar());

Zakładając, getValue()zwraca typ ogólny T. Ale to będzie miało kocięta, kiedy później przekażę to do metody, która się spodziewa Foo<Bar>. Czy casting jest jedynym sposobem na zrobienie tego?

Odpowiedzi:


280

Myślę, że musisz go rzucić, ale nie powinno być tak źle:

Foo<Bar> mockFoo = (Foo<Bar>) mock(Foo.class);
when(mockFoo.getValue()).thenReturn(new Bar());

34
Tak, ale nadal masz ostrzeżenie. Czy można uniknąć ostrzeżenia?
— odwl

12
@SuppressWarnings („niezaznaczone”)
— Qualidafial

18
Myślę, że jest to w pełni akceptowalne, ponieważ mówimy o próbnym obiekcie w teście jednostkowym.
— Magnilex,

1
@demaniak To w ogóle nie działa. W tym kontekście nie można używać dopasowań argumentów.
— Krzysztof Krasoń,

1
@demaniak To dobrze się skompiluje, ale po uruchomieniu testu wyrzuci InvalidUseOfMatchersException (który jest RuntimeException)
— Superole

277

Innym sposobem jest użycie @Mockadnotacji. Nie działa we wszystkich przypadkach, ale wygląda o wiele bardziej seksownie :)

Oto przykład:

@RunWith(MockitoJUnitRunner.class)
public class FooTests {

    @Mock
    public Foo<Bar> fooMock;

    @Test
    public void testFoo() {
        when(fooMock.getValue()).thenReturn(new Bar());
    }
}

MockitoJUnitRunnerInicjuje pola z adnotacjami @Mock.


3
jest to przestarzałe w 1.9.5. :( Wydaje mi się dużo czystszy.
— Kod Nowicjat

12
@CodeNovitiate Nie mogłem znaleźć adnotacji o wycofaniu na MockitoJUnitRunner i Mock w wersji 1.9.5. Co jest przestarzałe? (Tak, org.mockito.MockitoAnnotations.Mock jest przestarzałe, ale zamiast tego należy użyć org.mockito.Mock)
— neu242

12
Dobra robota, działało to dla mnie idealnie. To nie tylko „seksowniejsze”, pozwala uniknąć ostrzeżenia bez użycia SuppressWarnings. Ostrzeżenia istnieją z jakiegoś powodu, lepiej nie mieć w zwyczaju ich tłumienia. Dzięki!
— Nicole,

4
Jest jedna rzecz, której nie lubię używać @Mockzamiast mock(): pola są nadal puste w czasie budowy, więc nie mogę wstawić zależności w tym czasie i nie mogę sprawić, by pola były ostateczne. To pierwsze rozwiązanie można @Beforeoczywiście rozwiązać za pomocą opatrzonych komentarzem metod.
— Rüdiger Schulz

3
W celu inicjacji wystarczy wywołać MockitoAnnotations.initMocks (this);
— borjab

42

Zawsze możesz utworzyć klasę pośrednią / interfejs, który spełnia typ ogólny, który chcesz określić. Na przykład, jeśli Foo był interfejsem, możesz utworzyć następujący interfejs w swojej klasie testowej.

private interface FooBar extends Foo<Bar>
{
}

W sytuacjach, gdzie Foo jest non-final class, można po prostu rozszerzyć klasę z następującego kodu i zrobić to samo:

public class FooBar extends Foo<Bar>
{
}

Następnie możesz wykorzystać jeden z powyższych przykładów z następującym kodem:

Foo<Bar> mockFoo = mock(FooBar.class);
when(mockFoo.getValue()).thenReturn(new Bar());

4
Pod warunkiem, że Foojest to interfejs lub klasa nieoficjalna, wydaje się, że jest to dość eleganckie rozwiązanie. Dzięki.
— Tim Clemons,

Zaktualizowałem odpowiedź, aby zawierała również przykłady zajęć niekończących. Idealnie byłoby kodować na interfejsie, ale nie zawsze tak będzie. Dobry chwyt!
— dsingleton

16

Utwórz testową metodę narzędzia . Szczególnie przydatne, jeśli potrzebujesz go więcej niż raz.

@Test
public void testMyTest() {
    // ...
    Foo<Bar> mockFooBar = mockFoo();
    when(mockFooBar.getValue).thenReturn(new Bar());

    Foo<Baz> mockFooBaz = mockFoo();
    when(mockFooBaz.getValue).thenReturn(new Baz());

    Foo<Qux> mockFooQux = mockFoo();
    when(mockFooQux.getValue).thenReturn(new Qux());
    // ...
}

@SuppressWarnings("unchecked") // still needed :( but just once :)
private <T> Foo<T> mockFoo() {
    return mock(Foo.class);
}

Może rozszerzyć twoją odpowiedź, aby uczynić ogólną metodę użyteczności przechodzącą w klasie, którą chcesz wyśmiewać.
— William Dutton,

1
@WilliamDutton static <T> T genericMock(Class<? super T> classToMock) { return (T)mock(classToMock); }nie potrzebuje nawet jednego tłumienia :) Ale bądź ostrożny, Integer num = genericMock(Number.class)kompiluje, ale rzuca ClassCastException. Jest to przydatne tylko w najczęstszym G<P> mock = mock(G.class)przypadku.
— TWiStErRob

6

Zgadzam się, że nie należy tłumić ostrzeżeń w klasach lub metodach, ponieważ można przeoczyć inne, przypadkowo tłumione ostrzeżenia. Ale IMHO jest absolutnie uzasadnione, aby ukryć ostrzeżenie, które dotyczy tylko jednego wiersza kodu.

@SuppressWarnings("unchecked")
Foo<Bar> mockFoo = mock(Foo.class);

3

Oto ciekawy przypadek: metoda odbiera ogólną kolekcję i zwraca ogólną kolekcję tego samego typu podstawowego. Na przykład:

Collection<? extends Assertion> map(Collection<? extends Assertion> assertions);

Metodę tę można wyśmiewać za pomocą kombinacji Mockito anyCollectionOf matcher i Answer.

when(mockedObject.map(anyCollectionOf(Assertion.class))).thenAnswer(
     new Answer<Collection<Assertion>>() {
         @Override
         public Collection<Assertion> answer(InvocationOnMock invocation) throws Throwable {
             return new ArrayList<Assertion>();
         }
     });
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.