Più istruzioni RunWith in jUnit


113

Scrivo unit test e voglio usare JUnitParamsRunnere MockitoJUnitRunnerper una classe di test.

Purtroppo, quanto segue non funziona:

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

C'è un modo per utilizzare entrambi, Mockito e JUnitParams in una classe di test?



2
C'è un bell'esempio anche qui: blog.project13.pl/index.php/coding/1077/…
falsarella

Risposte:


110

Non è possibile farlo perché secondo le specifiche non è possibile inserire due volte la stessa annotazione sullo stesso elemento annotato.

Allora, qual è la soluzione? La soluzione è metterne solo uno @RunWith()con il corridore senza il quale non puoi stare e sostituirne l'altro con qualcos'altro. Nel tuo caso immagino che rimuoverai MockitoJUnitRunnere farai programmaticamente ciò che fa.

In effetti l'unica cosa che fa è eseguire:

MockitoAnnotations.initMocks(test);

all'inizio del test case. Quindi, la soluzione più semplice è inserire questo codice nel setUp()metodo:

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

Non ne sono sicuro, ma probabilmente dovresti evitare più chiamate di questo metodo usando flag:

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

Tuttavia, una soluzione migliore e riutilizzabile può essere implementata con le regole di JUnt.

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

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

Ora aggiungi la seguente riga alla tua classe di test:

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

e puoi eseguire questo test case con qualsiasi corridore tu voglia.


12
L'assegno mockInitializedè sbagliato. Vuoi avere una nuova derisione per ogni tetst.
BetaRide

1
@BetaRide, dipende dalle tue esigenze. A volte si desidera inizializzare mock ogni volta, a volte no.
AlexR

Se vuoi impostare una volta per file di classe puoi usare BeforeClass invece di Before, che verrà richiamato una e solo una volta per file di test.
InfernalRapture

56

A partire da JUnit 4.7 e Mockito 1.10.17, questa funzionalità è integrata; c'è una org.mockito.junit.MockitoRuleclasse. Puoi semplicemente importarlo e aggiungere la linea

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

alla tua classe di prova.


2
Per le versioni precedenti di Mockito (fino a 1.10.5 sembra), devi usare:@Rule public MockitoJUnitRule mockito = new MockitoJUnitRule(this);
Cliff Sun

MockitoAnnotations.initMocks(this)è molto lento a creare mock. Il modo più efficiente è usare @Runwith (MockitoJunitRunner.class)
ant2009

16

Questa soluzione funziona per ogni possibile corridore, non solo per questo esempio di mockito. Per esempio; per la primavera, basta cambiare le classi del corridore e aggiungere le annotazioni necessarie.

@RunWith(JUnitParamsRunner.class)
public class DatabaseModelTest {

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

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

DatabaseModelTestsarà gestito da JUnit. TestMockitoJUnitRunnerdipende da esso (per logica) e verrà eseguito all'interno del main in un @Testmetodo, durante la chiamata JUnitCore.runClasses(TestMockitoJUnitRunner.class). Questo metodo garantisce che il runner principale venga avviato correttamente prima che venga static class TestMockitoJUnitRunnereseguito il runner secondario, implementando efficacemente più @RunWithannotazioni annidate con classi di test dipendenti.

Anche su https://bekce.github.io/junit-multiple-runwith-dependent-tests


3
Chiamando JUnitCore.runClasses()senza controllare il risultato, rischi di mascherare gli errori dal test interno. assert(JUnitCore.runClasses(TestMockitoJUnitRunner.class).wasSuccessful());segnalerà almeno l'errore a te
Robotnik


2

Nel mio caso stavo cercando di prendere in giro qualche metodo in spring bean e

MockitoAnnotations.initMocks(test);

non funziona. Invece devi definire quel bean da costruire usando il metodo mock all'interno del tuo file xml come segue.

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

e aggiungi quel bean con autowired all'interno della tua classe di test come segue.

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

0

controlla questo link https://bekce.github.io/junit-multiple-runwith-dependent-tests/ utilizzando questo approccio ho combinato un @RunWith (Parameterized.class) - corridore esterno - con @RunWith (MockitoJUnitRunner.class) - corridore interno. L'unico aggiustamento che ho dovuto aggiungere è stato quello di rendere statiche le mie variabili membro nella classe esterna / corridore per renderle accessibili per il corridore / classe interno / annidato. buona fortuna e buon divertimento.


0

Volevo eseguire SWTBotJunit4ClassRunner e org.junit.runners.Parameterized allo stesso tempo, ho test parametrici e voglio screenshot quando il test SWT fallisce (la funzione screenshot è fornita da SWTBotJunit4ClassRunner ). La risposta di @ bekce è ottima e per prima cosa volevo seguire quella strada, ma era o bizzarro passare attraverso gli argomenti. O facendo il parametrizzato nella sottoclasse e perdendo le informazioni su quali test esatti hanno superato / fallito e hanno solo l'ultimo screenshot (poiché i nomi degli screenshot prendono il nome dal test stesso). Quindi in ogni caso è stato un po 'disordinato.

Nel mio caso SWTBotJunit4ClassRunner è abbastanza semplice, quindi ho clonato il codice sorgente della classe, gli ho dato il mio nome ParametrizedScreenshotRunner e dove l'originale stava estendendo il TestRunner , la mia classe sta estendendo la classe Parameterized quindi in sostanza posso usare il mio runner invece dei due precedenti. Ridotto il mio runner si estende sopra il runner parametrizzato mentre implementa la funzione screenshot su di esso, ora il mio test usa questo runner "ibrido" e tutti i test funzionano come previsto immediatamente (non c'è bisogno di cambiare nulla all'interno dei test).

Ecco come appare (per brevità ho rimosso tutti i commenti dall'elenco):

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);
        }
    }
}

-15

Puoi anche provare questo:

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

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

2
Questo non funzionerà, verrà elaborata solo l'annotazione della sottoclasse.
PaulNUK

non funziona - verrà presa in considerazione solo l'annotazione MockitoJUnitRunner
Przemek Bielicki
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.