Várias instruções RunWith no jUnit

113

Eu escrevo um teste de unidade e quero usar JUnitParamsRunnereMockitoJUnitRunner para uma aula de teste.

Infelizmente, o seguinte não funciona:

@RunWith(MockitoJUnitRunner.class)
@RunWith(JUnitParamsRunner.class)
public class DatabaseModelTest {
  // some tests
}

Existe uma maneira de usar Mockito e JUnitParams em uma classe de teste?

Hans-Helge
fonte
2
Há um bom exemplo aqui também: blog.project13.pl/index.php/coding/1077/…
falsarella

Respostas:

110

Você não pode fazer isso porque de acordo com as especificações, você não pode colocar a mesma anotação duas vezes no mesmo elemento anotado.

Então qual é a solução? A solução é colocar apenas um @RunWith()com o corredor sem o qual você não pode ficar e substituir outro por outra coisa. No seu caso, acho que você removerá MockitoJUnitRunnere fará programaticamente o que ele faz.

Na verdade, a única coisa que ele faz é:

MockitoAnnotations.initMocks(test);

no início do caso de teste. Portanto, a solução mais simples é colocar este código em setUp()método:

@Before
public void setUp() {
    MockitoAnnotations.initMocks(this);
}

Não tenho certeza, mas provavelmente você deve evitar várias chamadas desse método usando o sinalizador:

private boolean mockInitialized = false;
@Before
public void setUp() {
    if (!mockInitialized) {
        MockitoAnnotations.initMocks(this);
        mockInitialized = true;  
    }
}

Por melhor que seja, a solução reutilizável pode ser implementada com as regras da JUnt.

public class MockitoRule extends TestWatcher {
    private boolean mockInitialized = false;

    @Override
    protected void starting(Description d) {
        if (!mockInitialized) {
            MockitoAnnotations.initMocks(this);
            mockInitialized = true;  
        }
    }
}

Agora basta adicionar a seguinte linha à sua classe de teste:

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

e você pode executar este caso de teste com qualquer runner que desejar.

AlexR
fonte
12
A verificação mockInitializedestá errada. Você quer ter uma nova simulação para cada teste.
BetaRide
1
@BetaRide, depende de suas necessidades. Às vezes, você deseja inicializar o mock todas as vezes, às vezes não.
AlexR
Se você deseja configurar uma vez por arquivo de classe, pode usar BeforeClass em vez de Before, que será chamado uma vez e apenas uma vez por arquivo de teste.
InfernalRapture
56

A partir do JUnit 4.7 e do Mockito 1.10.17, essa funcionalidade é integrada; existe uma org.mockito.junit.MockitoRuleclasse. Você pode simplesmente importá-lo e adicionar a linha

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

para sua aula de teste.

Erica Kane
fonte
2
Para versões mais antigas do Mockito (até 1.10.5 ao que parece), você deve usar:@Rule public MockitoJUnitRule mockito = new MockitoJUnitRule(this);
Cliff Sun
MockitoAnnotations.initMocks(this)é muito lento para criar simulações. A maneira mais eficiente é usar o @Runwith (MockitoJunitRunner.class)
ant2009
16

Esta solução funciona para todos os corredores possíveis, não apenas para este exemplo do mockito. Por exemplo; para Spring, apenas altere as classes do runner e adicione as anotações necessárias.

@RunWith(JUnitParamsRunner.class)
public class DatabaseModelTest {

    @Test
    public void subRunner() throws Exception {
        JUnitCore.runClasses(TestMockitoJUnitRunner.class);
    }

    @RunWith(MockitoJUnitRunner.class)
    public static class TestMockitoJUnitRunner {
    }
}

DatabaseModelTestserá executado por JUnit. TestMockitoJUnitRunnerdepende dele (pela lógica) e será executado dentro do main em um @Testmétodo, durante a chamada JUnitCore.runClasses(TestMockitoJUnitRunner.class). Este método garante que o runner principal seja iniciado corretamente antes que o static class TestMockitoJUnitRunnersub-runner seja executado, implementando efetivamente várias @RunWithanotações aninhadas com classes de teste dependentes.

Também em https://bekce.github.io/junit-multiple-runwith-dependent-tests

Bekce
fonte
3
Ao chamar JUnitCore.runClasses()sem inspecionar o resultado, você corre o risco de mascarar os erros do teste interno. assert(JUnitCore.runClasses(TestMockitoJUnitRunner.class).wasSuccessful());irá, pelo menos, relatar o erro para você
Robotnik
2

No meu caso, eu estava tentando zombar de algum método no feijão da primavera e

MockitoAnnotations.initMocks(test);

não funciona. Em vez disso, você tem que definir aquele bean a ser construído usando o método mock dentro de seu arquivo xml como segue.

...
<bean id="classWantedToBeMocked" class="org.mockito.Mockito" factory-method="mock">
    <constructor-arg value="com.fullpath.ClassWantedToBeMocked" />
</bean>
...

e adicione aquele bean com autowired dentro de sua classe de teste como a seguir.

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(locations="file:springconfig.xml")
public class TestClass {
    ...
    @Autowired
    private ClassWantedToBeMocked classWantedToBeMocked;
    ...
    when(classWantedToBeMocked.methodWantedToBeMocked()).thenReturn(...);
    ...
}
Heungwoo
fonte
0

verifique este link https://bekce.github.io/junit-multiple-runwith-dependent-tests/ usando esta abordagem eu combinei um @RunWith (Parameterized.class) - corredor externo - com @RunWith (MockitoJUnitRunner.class) - corredor interno. O único ajuste que tive de adicionar foi tornar estáticas minhas variáveis ​​de membro na classe externa / runner para torná-las acessíveis para o runner / classe interna / aninhada. dê sorte e divirta-se.

Legna
fonte
0

Eu queria executar SWTBotJunit4ClassRunner e org.junit.runners.Parameterized ao mesmo tempo, tenho testes paramétricos e quero fazer capturas de tela quando o teste SWT falha (o recurso de captura de tela é fornecido pelo SWTBotJunit4ClassRunner ). A resposta de @bekce é ótima e primeiro queria seguir esse caminho, mas foi estranho passar pelos argumentos. Ou fazendo o parametrizado na subclasse e perdendo as informações de quais testes exatos passaram / falharam e ter apenas a última captura de tela (já que os nomes das capturas de tela obtêm o nome do próprio teste). De qualquer forma, foi um pouco confuso.

No meu caso o SWTBotJunit4ClassRunner é bastante simples, então eu clonei o código-fonte da classe, dei meu próprio nome ParametrizedScreenshotRunner e onde o original estava estendendo o TestRunner , minha classe está estendendo o Parameterized classe então posso usar meu próprio runner em vez dos dois anteriores. Fervido meu próprio runner se estende em cima do runner Parametrizado enquanto implemento o recurso de captura de tela em cima dele, agora meu teste usa este runner "híbrido" e todos os testes funcionam como esperado imediatamente (não há necessidade de alterar nada dentro dos testes).

É assim que parece (por uma questão de brevidade, removi todos os comentários da lista):

package mySwtTests;

import org.junit.runners.Parameterized;
import org.eclipse.swtbot.swt.finder.junit.ScreenshotCaptureListener;
import org.junit.runner.notification.RunListener;
import org.junit.runner.notification.RunNotifier;

public class ParametrizedScreenshotRunner extends TestRu Parameterized {

    public ParametrizedScreenshotRunner(Class<?> klass) throws Throwable {
        super(klass);
    }

    public void run(RunNotifier notifier) {
        RunListener failureSpy = new ScreenshotCaptureListener();
        notifier.removeListener(failureSpy); // remove existing listeners that could be added by suite or class runners
        notifier.addListener(failureSpy);
        try {
            super.run(notifier);
        } finally {
            notifier.removeListener(failureSpy);
        }
    }
}
muni764
fonte
-15

Você também pode tentar isto:

@RunWith(JUnitParamsRunner.class)
public class AbstractTestClass {
  // some tests
}

@RunWith(MockitoJUnitRunner.class)
public class DatabaseModelTest extends AbstractTestClass {
  // some tests
}
Valentin Uveges
fonte
2
Isso não funcionará, apenas a anotação da subclasse será processada.
PaulNUK
não funciona - apenas a anotação MockitoJUnitRunner será levada em consideração
Przemek Bielicki