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
MockitoJUnitRunner
. Para obter mais informações sobre as diferenças, consulte a pergunta em stackoverflow.com/questions/10806345/… e minha resposta a ela.Collaborator collab = mock(Collaborator.class)
, na minha opinião, esta forma é certamente uma abordagem válida. Embora isso possa tender a ser prolixo, você pode obter maior compreensão e refatoração dos testes. Ambas as formas têm seus prós e contras, ainda não decidi qual abordagem é melhor. Amyway, sempre é possível escrever uma porcaria e provavelmente depende do contexto e do codificador.Existe agora (a partir da v1.10.7) uma quarta maneira de instanciar mocks, que é usando uma regra JUnit4 chamada MockitoRule .
JUnit procura por subclasses de TestRule anotadas com @Rule e as usa para agrupar as instruções de teste que o Runner fornece . O resultado disso é que você pode extrair métodos @Before, métodos @After e até mesmo tentar ... capturar wrappers em regras. Você pode até interagir com eles de dentro do seu teste, da maneira que ExpectedException faz.
MockitoRule se comporta quase exatamente como MockitoJUnitRunner , exceto que você pode usar qualquer outro runner, como Parameterized (que permite que seus construtores de teste recebam argumentos para que seus testes possam ser executados várias vezes) ou o executor de teste do Robolectric (para que seu classloader possa fornecer substituições Java para classes nativas Android). Isso o torna estritamente mais flexível para uso nas versões JUnit e Mockito recentes.
Em suma:
Mockito.mock()
: Invocação direta sem suporte de anotação ou validação de uso.MockitoAnnotations.initMocks(this)
: Suporte de anotação, sem validação de uso.MockitoJUnitRunner
: Suporte de anotação e validação de uso, mas você deve usar esse runner.MockitoRule
: Suporte de anotação e validação de uso com qualquer executor JUnit.Veja também: Como funciona o JUnit @Rule?
fonte
@get:Rule val mockitoRule: MockitoRule = MockitoJUnit.rule()
Existe uma maneira legal de fazer isso.
Se for um teste de unidade, você pode fazer o seguinte:
EDITAR: Se for um teste de integração, você pode fazer isso (não se destina a ser usado dessa forma com Spring. Apenas mostre que você pode inicializar simulações com diferentes Runners):
fonte
MockitoAnnotations & the runner foram bem discutidos acima, então vou jogar meus dois centavos para os não amados:
Eu uso isso porque acho que é um pouco mais descritivo e prefiro (não banir totalmente) os testes de unidade não usar variáveis de membro, pois gosto que meus testes sejam (o máximo que podem) independentes.
fonte
Um pequeno exemplo para JUnit 5 Jupiter, o "RunWith" foi removido, agora você precisa usar as extensões usando a anotação "@ExtendWith".
fonte
As outras respostas são ótimas e contêm mais detalhes se você quiser / precisar delas.
Além desses, gostaria de adicionar um TL; DR:
@RunWith(MockitoJUnitRunner.class)
@Rule public MockitoRule rule = MockitoJUnit.rule();
@Before public void initMocks() { MockitoAnnotations.initMocks(this); }
X x = mock(X.class)
(1) e (2) e (3) são mutuamente exclusivos.
(4) pode ser usado em combinação com os outros.
fonte