@RunWith (MockitoJUnitRunner.class) vs MockitoAnnotations.initMocks (this)

118

Ao escrever um novo teste jUnit4, estou pensando se devo usar @RunWith (MockitoJUnitRunner.class) ou MockitoAnnotations.initMocks (this) .

Eu criei um novo teste e o assistente gerou automaticamente um teste com o Runner. Javadocs para MockitoJUnitRunner afirmam o seguinte:

Compatível com JUnit 4.4 e superior, este runner adiciona o seguinte comportamento:

Inicializa mocks anotados com Mock, de modo que o uso explícito de MockitoAnnotations.initMocks (Object) não é necessário. Mocks são inicializados antes de cada método de teste. valida o uso da estrutura após cada método de teste.

Não está claro para mim se usar o Runner tem alguma vantagem sobre o método initMocks () que tenho usado no passado.

Quaisquer pensamentos ou links serão apreciados!

Oceano azul
fonte

Respostas:

147

MockitoJUnitRunnerdá a você validação automática de uso do framework, bem como uma automática initMocks().

Vale a pena ter a validação automática do uso do framework. Você terá um relatório melhor se cometer um desses erros.

  • Você chama o whenmétodo estático , mas não conclui o stub com uma correspondência thenReturn, thenThrowou then. (Erro 1 no código abaixo)

  • Você chama verifyum mock, mas se esquece de fornecer a chamada de método que está tentando verificar. (Erro 2 no código abaixo)

  • Você chamar o whenmétodo depois doReturn, doThrowou doAnswere passar um mock, mas se esqueça de fornecer o método que você está tentando topo. (Erro 3 no código abaixo)

Se você não tem validação de uso do framework, esses erros não são relatados até a seguinte chamada para um método Mockito. Pode ser

  • no mesmo método de teste (como o erro 1 abaixo),
  • no próximo método de teste (como o erro 2 abaixo),
  • na próxima aula de teste.

Se eles ocorrerem no último teste executado (como o erro 3 abaixo), eles não serão relatados.

Veja como cada um desses tipos de erros pode ser exibido. Suponha aqui que o JUnit executa esses testes na ordem em que estão listados aqui.

@Test
public void test1() {

    // ERROR 1
    // This compiles and runs, but it's an invalid use of the framework because 
    // Mockito is still waiting to find out what it should do when myMethod is called.
    // But Mockito can't report it yet, because the call to thenReturn might 
    // be yet to happen.
    when(myMock.method1());

    doSomeTestingStuff();

    // ERROR 1 is reported on the following line, even though it's not the line with
    // the error.
    verify(myMock).method2();

}

@Test
public void test2() {

    doSomeTestingStuff();

    // ERROR 2
    // This compiles and runs, but it's an invalid use of the framework because
    // Mockito doesn't know what method call to verify.  But Mockito can't report 
    // it yet, because the call to the method that's being verified might 
    // be yet to happen.
    verify(myMock);
}

@Test
public void test3() {

    // ERROR 2 is reported on the following line, even though it's not even in 
    // the same test as the error.
    doReturn("Hello").when(myMock).method1();


    // ERROR 3
    // This compiles and runs, but it's an invalid use of the framework because
    // Mockito doesn't know what method call is being stubbed.  But Mockito can't 
    // report it yet, because the call to the method that's being stubbed might 
    // be yet to happen.

    doReturn("World").when(myMock);

    doSomeTestingStuff(); 

    //  ERROR 3 is never reported, because there are no more Mockito calls. 
}

Quando escrevi esta resposta pela primeira vez, há mais de cinco anos, escrevi

Portanto, eu recomendaria o uso de MockitoJUnitRunnersempre que possível. No entanto, como Tomasz Nurkiewicz apontou corretamente, você não pode usá-lo se precisar de outro runner JUnit, como o Spring.

Minha recomendação agora mudou. A equipe Mockito adicionou um novo recurso desde que escrevi esta resposta pela primeira vez. É uma regra JUnit, que executa exatamente a mesma função que o MockitoJUnitRunner. Mas é melhor, porque não impede o uso de outros corredores.

Incluir

@Rule 
public MockitoRule rule = MockitoJUnit.rule();

em sua classe de teste. Isso inicializa os mocks e automatiza a validação da estrutura; assim como MockitoJUnitRunnerfaz. Mas agora, você pode usar SpringJUnit4ClassRunnerou qualquer outro JUnitRunner também. Do Mockito 2.1.0 em diante, existem opções adicionais que controlam exatamente que tipo de problemas são relatados.

Dawood ibn Kareem
fonte
eu definitivamente não posso dizer que eles são iguais. em um caso de teste, a configuração do junit runner falha para mim e não injeta meus mocks corretamente, a menos que eu faça a configuração do initMocks
dtc
Estamos usando o testng 6.8.8 + mockito 1.10.19 e, obviamente, não podemos usar o MockitoJUnitRunner, mas o framework de validação ainda funciona! E funciona exatamente como @David Wallace. Alguém pode explicar? É porque ainda temos callbacks @ Before * e MockitoAnnotations.initMocks (this)?
yuranos
@ yuranos87 Parece algo que você deveria fazer como uma nova pergunta. Não se esqueça de incluir seu código ao fazer isso - é um pouco inútil perguntar "por que este código faz XYZ" se você não mostra o código.
Dawood ibn Kareem
1
usando a solução TestRunner supera o @Rule de longe
Regra
1
@alexandroid Minha melhor sugestão é que você escreva sua própria resposta, usando @ExtendWith. Não é realmente algo que eu conheça. A grande vantagem do Stack Overflow é que em uma pergunta como essa, você pode acabar com várias respostas corretas.
Dawood ibn Kareem
24

Usar o runner permite economizar um pouco de codificação (sem necessidade de @Beforemétodo). Por outro lado, usar um runner às vezes não é possível, ou seja, quando você já está usando um, como SpringJUnit4ClassRunner.

É isso aí. É apenas uma questão de preferência.

Tomasz Nurkiewicz
fonte
2
Além da linha initMocks (), o método @Before ainda seria necessário para qualquer outra configuração, certo?
OceanBlue
2
@OceanBlue: Claro, se o seu @Beforemétodo contiver qualquer coisa, exceto initMocks()você terá que preservá-lo após a migração para o runner.
Tomasz Nurkiewicz
A resposta de David Wallace sobre a validação do framework responde totalmente à minha pergunta, então eu aceitei essa, mas +1 por apontar que este runner não pode ser usado com outro, como o Spring. Obrigado!
OceanBlue
1
Estou usando o Spring Boot e posso dizer que SpringJUnit4ClassRunnerinicializa automaticamente os mocks para mim. Eu não sei sobre a simples Primavera, no entanto.
gustavohenke