Für die Mock-Initialisierung sind die Verwendung des Läufers oder der MockitoAnnotations.initMocksstreng äquivalenten Lösungen. Aus dem Javadoc des 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.
Die erste Lösung (mit dem MockitoAnnotations.initMocks) kann verwendet werden, wenn Sie bereits einen bestimmten Läufer ( SpringJUnit4ClassRunnerzum Beispiel) für Ihren Testfall konfiguriert haben .
Die zweite Lösung (mit der MockitoJUnitRunner) ist die klassischere und meine Lieblingslösung. Der Code ist einfacher. Die Verwendung eines Runners bietet den großen Vorteil der automatischen Validierung der Framework-Nutzung (beschrieben von @David Wallace in dieser Antwort ).
Beide Lösungen ermöglichen es, die Verspottungen (und Spione) zwischen den Testmethoden zu teilen. In Verbindung mit dem @InjectMocksermöglichen sie das schnelle Schreiben von Unit-Tests. Der Kesselplatten-Verspottungscode wird reduziert, die Tests sind leichter zu lesen. Beispielsweise:
@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));
}
}
Vorteile: Der Code ist minimal
Nachteile: Schwarze Magie. IMO liegt es hauptsächlich an der Annotation @InjectMocks. Mit dieser Anmerkung "Sie verlieren den Schmerz des Codes" (siehe die großartigen Kommentare von @Brice )
Die dritte Lösung besteht darin, für jede Testmethode ein Modell zu erstellen. Es erlaubt, wie von @mlk in seiner Antwort erklärt, einen " in sich geschlossenen Test " zu haben.
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));
}
}
Vorteile: Sie zeigen deutlich, wie Ihre API funktioniert (BDD ...)
Nachteile: Es gibt mehr Boilerplate-Code. (Die Verspottung Schöpfung)
Meine Empfehlung ist ein Kompromiss. Verwenden Sie die @MockAnmerkung mit dem @RunWith(MockitoJUnitRunner.class), aber nicht das @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));
}
}
Vorteile: Sie zeigen deutlich, wie Ihre API funktioniert (wie meine ArticleManagerinstanziiert wird). Kein Boilerplate-Code.
Nachteile: Der Test ist nicht eigenständig, weniger Schmerzen im Code