@RunWith (MockitoJUnitRunner.class) vs MockitoAnnotations.initMocks (this)

118

Pisząc nowy test jUnit4 zastanawiam się, czy użyć @RunWith (MockitoJUnitRunner.class) czy MockitoAnnotations.initMocks (this) .

Utworzyłem nowy test, a kreator automatycznie wygenerował test z Runnerem. Javadocs dla MockitoJUnitRunner zawierają następujące informacje:

Kompatybilny z JUnit 4.4 i nowszymi wersjami, ten runner dodaje następujące zachowanie:

Inicjuje makiety z adnotacjami Mock, więc jawne użycie MockitoAnnotations.initMocks (Object) nie jest konieczne. Mocky są inicjowane przed każdą metodą testową. sprawdza użycie frameworka po każdej metodzie testowej.

Nie jest dla mnie jasne, czy użycie Runnera ma jakąkolwiek przewagę nad metodą initMocks () , z której korzystałem w przeszłości.

Wszelkie uwagi lub linki będą mile widziane!

Niebieski ocean
źródło

Odpowiedzi:

147

MockitoJUnitRunnerzapewnia automatyczną weryfikację użycia frameworka, a także automatyczne initMocks().

Warto mieć automatyczną walidację użycia frameworka. Zapewnia lepsze raportowanie, jeśli popełnisz jeden z tych błędów.

  • Nazywasz statyczną whenmetodę, ale nie zakończyć stubbing z dopasowaną thenReturn, thenThrowlub then. (Błąd 1 w poniższym kodzie)

  • Dzwonisz verifyna makiety, ale zapomnij podać wywołanie metody, które starają się zweryfikować. (Błąd 2 w poniższym kodzie)

  • Wywołujemy whenmetodę po doReturn, doThrowalbo doAnsweri zdać makiety, ale zapomnij dostarczyć metody, które starają się odgałęzienie. (Błąd 3 w poniższym kodzie)

Jeśli nie masz weryfikacji użycia struktury, te błędy nie są zgłaszane do momentu następującego wywołania metody Mockito. To może być

  • w tej samej metodzie testowej (jak błąd 1 poniżej),
  • w następnej metodzie testowej (jak błąd 2 poniżej),
  • w następnej klasie testowej.

Jeśli wystąpią w ostatnim przeprowadzonym teście (jak błąd 3 poniżej), w ogóle nie zostaną zgłoszone.

Oto, jak może wyglądać każdy z tych typów błędów. Załóżmy, że JUnit przeprowadza te testy w kolejności, w jakiej są tutaj wymienione.

@Test
public void test1() {

    // ERROR 1
    // This compiles and runs, but it's an invalid use of the framework because 
    // Mockito is still waiting to find out what it should do when myMethod is called.
    // But Mockito can't report it yet, because the call to thenReturn might 
    // be yet to happen.
    when(myMock.method1());

    doSomeTestingStuff();

    // ERROR 1 is reported on the following line, even though it's not the line with
    // the error.
    verify(myMock).method2();

}

@Test
public void test2() {

    doSomeTestingStuff();

    // ERROR 2
    // This compiles and runs, but it's an invalid use of the framework because
    // Mockito doesn't know what method call to verify.  But Mockito can't report 
    // it yet, because the call to the method that's being verified might 
    // be yet to happen.
    verify(myMock);
}

@Test
public void test3() {

    // ERROR 2 is reported on the following line, even though it's not even in 
    // the same test as the error.
    doReturn("Hello").when(myMock).method1();


    // ERROR 3
    // This compiles and runs, but it's an invalid use of the framework because
    // Mockito doesn't know what method call is being stubbed.  But Mockito can't 
    // report it yet, because the call to the method that's being stubbed might 
    // be yet to happen.

    doReturn("World").when(myMock);

    doSomeTestingStuff(); 

    //  ERROR 3 is never reported, because there are no more Mockito calls. 
}

Kiedy po raz pierwszy napisałem tę odpowiedź ponad pięć lat temu, napisałem

Dlatego polecałbym korzystanie z opcji MockitoJUnitRunnerwszędzie tam, gdzie to możliwe. Jednak, jak słusznie zauważył Tomasz Nurkiewicz, nie możesz z niego skorzystać, jeśli potrzebujesz innego biegacza JUnit, na przykład wiosennego.

Moja rekomendacja się teraz zmieniła. Zespół Mockito dodał nową funkcję, odkąd po raz pierwszy napisałem tę odpowiedź. Jest to reguła JUnit, która wykonuje dokładnie taką samą funkcję jak MockitoJUnitRunner. Ale jest lepiej, ponieważ nie wyklucza korzystania z innych biegaczy.

Zawierać

@Rule 
public MockitoRule rule = MockitoJUnit.rule();

w klasie testowej. To inicjalizuje makiety i automatyzuje walidację struktury; tak jak MockitoJUnitRunnerrobi. Ale teraz możesz również użyć SpringJUnit4ClassRunnerlub dowolnego innego JUnitRunner. Od wersji Mockito 2.1.0 dostępne są dodatkowe opcje, które dokładnie kontrolują, jakie rodzaje problemów są zgłaszane.

Dawood ibn Kareem
źródło
zdecydowanie nie mogę powiedzieć, że są takie same. w jednym przypadku testowym konfiguracja junit runner kończy się niepowodzeniem i nie wstrzykuje poprawnie moich makiet, chyba że wykonam konfigurację initMocks
dtc
Używamy testng 6.8.8 + mockito 1.10.19 i oczywiście nie możemy używać MockitoJUnitRunner, ale framework walidacyjny nadal działa! I działa dokładnie tak, jak @David Wallace. Czy ktoś może wyjaśnić? Czy to dlatego, że nadal mamy wywołania zwrotne @ Before * i MockitoAnnotations.initMocks (this)?
yuranos
@ yuranos87 Brzmi jak coś, co powinieneś zadać jako nowe pytanie. Nie zapomnij dołączyć kodu, kiedy to robisz - nie ma sensu pytać „dlaczego ten kod robi XYZ”, jeśli nie pokazujesz kodu.
Dawood ibn Kareem,
1
korzystanie z rozwiązania TestRunner znacznie przewyższa @Rule
Reguła
1
@alexandroid Moją najlepszą sugestią jest napisanie własnej odpowiedzi za pomocą @ExtendWith. To nie jest coś, o czym wiem. Wspaniałą rzeczą w przepełnieniu stosu jest to, że w przypadku takiego pytania możesz otrzymać wiele poprawnych odpowiedzi.
Dawood ibn Kareem
24

Korzystanie z runnera pozwala zaoszczędzić trochę kodowania (nie ma potrzeby stosowania @Beforemetody). Z drugiej strony korzystanie z biegacza jest czasami niemożliwe, np. Gdy już go używasz, np SpringJUnit4ClassRunner.

Otóż ​​to. To tylko kwestia preferencji.

Tomasz Nurkiewicz
źródło
2
Poza linią initMocks () metoda @Before byłaby nadal potrzebna do każdej innej konfiguracji, prawda?
OceanBlue
2
@OceanBlue: Oczywiście, jeśli twoja @Beforemetoda zawierała cokolwiek poza tym, initMocks()że musisz ją zachować po migracji do runnera.
Tomasz Nurkiewicz
Odpowiedź Davida Wallace'a na temat walidacji frameworka w pełni odpowiada na moje pytanie, więc zaakceptowałem to, ale +1 za wskazanie, że tego biegacza nie można używać z innym, takim jak Spring. Dzięki!
OceanBlue
1
Używam Spring Boot i mogę powiedzieć, że SpringJUnit4ClassRunnerautomatycznie inicjuje makiety. Nie wiem jednak o zwykłej wiośnie.
gustavohenke