W przypadku inicjalizacji mocków użycie runner lub the MockitoAnnotations.initMocks
jest rozwiązaniami ściśle równoważnymi. Z javadoc MockitoJUnitRunner :
JUnit 4.5 runner initializes mocks annotated with Mock, so that explicit usage of MockitoAnnotations.initMocks(Object) is not necessary. Mocks are initialized before each test method.
Pierwszego rozwiązania (z MockitoAnnotations.initMocks
) można użyć, gdy skonfigurowałeś już określonego runnera ( SpringJUnit4ClassRunner
na przykład) w swoim przypadku testowym.
Drugie rozwiązanie (z MockitoJUnitRunner
) jest bardziej klasyczne i moje ulubione. Kod jest prostszy. Korzystanie z runnera zapewnia ogromną zaletę automatycznej walidacji użycia frameworka (opisanej przez @David Wallace w tej odpowiedzi ).
Oba rozwiązania pozwalają na dzielenie się fałszywkami (i szpiegami) między metodami testowymi. W połączeniu z @InjectMocks
, pozwalają na bardzo szybkie pisanie testów jednostkowych. Zredukowano standardowy kod szyfrujący, testy są łatwiejsze do odczytania. Na przykład:
@RunWith(MockitoJUnitRunner.class)
public class ArticleManagerTest {
@Mock private ArticleCalculator calculator;
@Mock(name = "database") private ArticleDatabase dbMock;
@Spy private UserProvider userProvider = new ConsumerUserProvider();
@InjectMocks private ArticleManager manager;
@Test public void shouldDoSomething() {
manager.initiateArticle();
verify(database).addListener(any(ArticleListener.class));
}
@Test public void shouldDoSomethingElse() {
manager.finishArticle();
verify(database).removeListener(any(ArticleListener.class));
}
}
Zalety: kod jest minimalny
Wady: Czarna magia. IMO to głównie zasługa adnotacji @InjectMocks. Dzięki tej adnotacji „tracisz ból kodu” (zobacz świetne komentarze @Brice )
Trzecie rozwiązanie polega na stworzeniu makiety na każdej metodzie testowej. Pozwala, jak wyjaśnił @mlk w swojej odpowiedzi, na „ samodzielny test ”.
public class ArticleManagerTest {
@Test public void shouldDoSomething() {
// given
ArticleCalculator calculator = mock(ArticleCalculator.class);
ArticleDatabase database = mock(ArticleDatabase.class);
UserProvider userProvider = spy(new ConsumerUserProvider());
ArticleManager manager = new ArticleManager(calculator,
userProvider,
database);
// when
manager.initiateArticle();
// then
verify(database).addListener(any(ArticleListener.class));
}
@Test public void shouldDoSomethingElse() {
// given
ArticleCalculator calculator = mock(ArticleCalculator.class);
ArticleDatabase database = mock(ArticleDatabase.class);
UserProvider userProvider = spy(new ConsumerUserProvider());
ArticleManager manager = new ArticleManager(calculator,
userProvider,
database);
// when
manager.finishArticle();
// then
verify(database).removeListener(any(ArticleListener.class));
}
}
Plusy: Wyraźnie pokazujesz, jak działa twój interfejs API (BDD ...)
Wady: jest więcej standardowych kodów. (Kreacja pozorów)
Moja rekomendacja to kompromis. Użyj @Mock
adnotacji z @RunWith(MockitoJUnitRunner.class)
, ale nie używaj @InjectMocks
:
@RunWith(MockitoJUnitRunner.class)
public class ArticleManagerTest {
@Mock private ArticleCalculator calculator;
@Mock private ArticleDatabase database;
@Spy private UserProvider userProvider = new ConsumerUserProvider();
@Test public void shouldDoSomething() {
// given
ArticleManager manager = new ArticleManager(calculator,
userProvider,
database);
// when
manager.initiateArticle();
// then
verify(database).addListener(any(ArticleListener.class));
}
@Test public void shouldDoSomethingElse() {
// given
ArticleManager manager = new ArticleManager(calculator,
userProvider,
database);
// when
manager.finishArticle();
// then
verify(database).removeListener(any(ArticleListener.class));
}
}
Zalety: jasno pokazujesz, jak działa twój interfejs API (jak ArticleManager
tworzony jest mój ). Brak kodu standardowego.
Wady: test nie jest samodzielny, mniej bólu związanego z kodem
MockitoJUnitRunner
. Aby uzyskać więcej informacji na temat różnic, zobacz pytanie na stackoverflow.com/questions/10806345/ ... i moją odpowiedź na nie.Collaborator collab = mock(Collaborator.class)
, moim zdaniem w ten sposób, z pewnością jest to słuszne podejście. Chociaż może to być rozwlekłe, możesz zyskać na zrozumiałości i poprawności testów. Oba sposoby mają swoje wady i zalety, nie zdecydowałem jeszcze, które podejście jest lepsze. Amyway zawsze można pisać bzdury i prawdopodobnie zależy to od kontekstu i kodera.Obecnie (od wersji 1.10.7) istnieje czwarty sposób tworzenia instancji makiet, który wykorzystuje regułę JUnit4 o nazwie MockitoRule .
JUnit szuka podklas TestRule z adnotacjami @Rule i używa ich do pakowania instrukcji testowych, które dostarcza Runner . W rezultacie możesz wyodrębnić metody @Before, metody @After, a nawet spróbować ... przechwycić opakowania do reguł. Możesz nawet wchodzić z nimi w interakcje z poziomu testu, tak jak robi to ExpectedException .
MockitoRule zachowuje się prawie tak samo, jak MockitoJUnitRunner , z tym wyjątkiem, że możesz użyć dowolnego innego programu uruchamiającego , takiego jak Sparametryzowany (który umożliwia konstruktorom testów pobieranie argumentów, dzięki czemu testy mogą być uruchamiane wiele razy) lub program uruchamiający testy Robolectric (dzięki czemu jego classloader może zapewnić zamienniki Java dla klas natywnych systemu Android). To sprawia, że jest bardziej elastyczny w użyciu w najnowszych wersjach JUnit i Mockito.
W podsumowaniu:
Mockito.mock()
: Bezpośrednie wywołanie bez obsługi adnotacji lub weryfikacji użycia.MockitoAnnotations.initMocks(this)
: Obsługa adnotacji, brak weryfikacji użycia.MockitoJUnitRunner
: Obsługa adnotacji i walidacja użycia, ale musisz użyć tego elementu uruchamiającego.MockitoRule
: Obsługa adnotacji i walidacja użycia z dowolnym runner'em JUnit.Zobacz też: Jak działa JUnit @Rule?
źródło
@get:Rule val mockitoRule: MockitoRule = MockitoJUnit.rule()
Jest na to zgrabny sposób.
Jeśli jest to test jednostkowy, możesz to zrobić:
EDYCJA: Jeśli jest to test integracji, możesz to zrobić (nie jest przeznaczone do używania w ten sposób w Spring. Pokaż tylko, że możesz zainicjować makiety z różnymi Runnerami):
źródło
Mockito Adnotacje i biegacz zostały dobrze omówione powyżej, więc zamierzam dorzucić moją podwójną pensję dla niekochanych:
Używam tego, ponieważ uważam, że jest to trochę bardziej opisowe i wolę (nie z prawej strony) testy jednostkowe, aby nie używać zmiennych składowych, ponieważ lubię, aby moje testy były (na tyle, na ile mogą) samodzielne.
źródło
Mały przykład dla JUnit 5 Jupiter, "RunWith" został usunięty. Teraz musisz używać rozszerzeń używając adnotacji "@ExtendWith".
źródło
Pozostałe odpowiedzi są świetne i zawierają więcej szczegółów, jeśli ich chcesz / potrzebujesz.
Oprócz tego chciałbym dodać TL; DR:
@RunWith(MockitoJUnitRunner.class)
@Rule public MockitoRule rule = MockitoJUnit.rule();
@Before public void initMocks() { MockitoAnnotations.initMocks(this); }
X x = mock(X.class)
(1) i (2) i (3) wykluczają się wzajemnie.
(4) mogą być używane w połączeniu z innymi.
źródło