Per l'inizializzazione mock , utilizzando il runner o MockitoAnnotations.initMockssono soluzioni strettamente equivalenti. Dal javadoc di 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.
La prima soluzione (con il MockitoAnnotations.initMocks) potrebbe essere utilizzata quando hai già configurato un runner specifico ( SpringJUnit4ClassRunnerad esempio) sul tuo test case.
La seconda soluzione (con il MockitoJUnitRunner) è la più classica e la mia preferita. Il codice è più semplice. L'utilizzo di un runner offre il grande vantaggio della convalida automatica dell'utilizzo del framework (descritto da @David Wallace in questa risposta ).
Entrambe le soluzioni consentono di condividere i mock (e le spie) tra i metodi di prova. Insieme a @InjectMocks, consentono di scrivere test unitari molto rapidamente. Il codice di mocking boilerplate è ridotto, i test sono più facili da leggere. Per esempio:
@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));
}
}
Pro: il codice è minimo
Contro: Black magic. IMO è principalmente dovuto all'annotazione @InjectMocks. Con questa annotazione " perdi il dolore del codice" (vedi gli ottimi commenti di @Brice )
La terza soluzione è creare il tuo mock su ogni metodo di test. Permette come spiegato da @mlk nella sua risposta di avere un " test autonomo ".
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));
}
}
Pro: dimostri chiaramente come funziona la tua API (BDD ...)
Contro: c'è più codice boilerplate. (La creazione beffa)
La mia raccomandazione è un compromesso. Utilizza l' @Mockannotazione con @RunWith(MockitoJUnitRunner.class), ma non utilizzare @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));
}
}
Pro: dimostri chiaramente come funziona la tua API (come ArticleManagerviene istanziata la mia ). Nessun codice boilerplate.
Contro: il test non è autonomo, meno problemi di codice