Per l'inizializzazione mock , utilizzando il runner o MockitoAnnotations.initMocks
sono 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 ( SpringJUnit4ClassRunner
ad 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' @Mock
annotazione 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 ArticleManager
viene istanziata la mia ). Nessun codice boilerplate.
Contro: il test non è autonomo, meno problemi di codice