Für die Mock-Initialisierung sind die Verwendung des Läufers oder der MockitoAnnotations.initMocks
streng ä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 ( SpringJUnit4ClassRunner
zum 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 @InjectMocks
ermö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 @Mock
Anmerkung 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 ArticleManager
instanziiert wird). Kein Boilerplate-Code.
Nachteile: Der Test ist nicht eigenständig, weniger Schmerzen im Code