Para a inicialização de simulações , usar o runner ou MockitoAnnotations.initMocks
são soluções estritamente equivalentes. Do javadoc do 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.
A primeira solução (com MockitoAnnotations.initMocks
) pode ser usada quando você já configurou um runner específico ( SpringJUnit4ClassRunner
por exemplo) em seu caso de teste.
A segunda solução (com o MockitoJUnitRunner
) é a mais clássica e minha favorita. O código é mais simples. Usando um corredor oferece a grande vantagem de validação automática do uso do quadro (descrito por @ David Wallace em esta resposta ).
Ambas as soluções permitem compartilhar os mocks (e espiões) entre os métodos de teste. Juntamente com o @InjectMocks
, eles permitem escrever testes de unidade muito rapidamente. O código de simulação padrão é reduzido, os testes são mais fáceis de ler. Por exemplo:
@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));
}
}
Prós: o código é mínimo
Contras: magia negra. IMO é principalmente devido à anotação @InjectMocks. Com esta anotação "você perde a dor do código" (veja os ótimos comentários de @Brice )
A terceira solução é criar seu mock em cada método de teste. Ele permite, conforme explicado por @mlk em sua resposta, ter um " teste independente ".
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));
}
}
Prós: você demonstra claramente como sua API funciona (BDD ...)
Contras: há mais código clichê. (A criação de mocks)
Minha recomendação é um compromisso. Use a @Mock
anotação com @RunWith(MockitoJUnitRunner.class)
, mas não use @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));
}
}
Prós: você demonstra claramente como sua API funciona (como o my ArticleManager
é instanciado). Nenhum código clichê.
Contras: o teste não é independente, menos dor de código