Wiele instrukcji RunWith w jUnit

113

Piszę testy jednostkowe i chcę używać JUnitParamsRunneri MockitoJUnitRunnerdla jednej klasy testowej.

Niestety nie działa:

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

Czy istnieje sposób na użycie obu, Mockito i JUnitParams w jednej klasie testowej?

Hans-Helge
źródło
2
Jest tu również fajny przykład: blog.project13.pl/index.php/coding/1077/…
falsarella

Odpowiedzi:

110

Nie możesz tego zrobić, ponieważ zgodnie ze specyfikacją nie możesz umieścić tej samej adnotacji dwa razy na tym samym elemencie z adnotacją.

Więc jakie jest rozwiązanie? Rozwiązaniem jest postawienie tylko jednego @RunWith()z biegaczem, bez którego nie można się obejść i zastąpienie drugiego czymś innym. W twoim przypadku myślę, że usuniesz MockitoJUnitRunneri zrobisz programowo to, co robi.

Właściwie jedyne, co robi, to działa:

MockitoAnnotations.initMocks(test);

na początku przypadku testowego. Zatem najprostszym rozwiązaniem jest umieszczenie tego kodu w setUp()metodzie:

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

Nie jestem pewien, ale prawdopodobnie powinieneś unikać wielokrotnego wywoływania tej metody za pomocą flagi:

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

Jednak lepsze rozwiązanie wielokrotnego użytku można wdrożyć zgodnie z regułami JUnt.

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

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

Teraz po prostu dodaj następujący wiersz do swojej klasy testowej:

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

i możesz uruchomić ten przypadek testowy z dowolnym biegaczem.

AlexR
źródło
12
Czek mockInitializedjest zły. Chcesz mieć świeżą próbę na każdy test.
BetaRide
1
@BetaRide, to zależy od Twoich potrzeb. Czasami chcesz za każdym razem zainicjować mock, czasami nie.
AlexR
Jeśli chcesz skonfigurować tylko raz na plik klasy, możesz użyć BeforeClass zamiast Before, które zostanie wywołane raz i tylko raz na plik testowy.
InfernalRapture,
56

Począwszy od JUnit 4.7 i Mockito 1.10.17, ta funkcja jest wbudowana; jest org.mockito.junit.MockitoRuleklasa. Możesz go po prostu zaimportować i dodać linię

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

do klasy testowej.

Erica Kane
źródło
2
W przypadku starszych wersji Mockito (do 1.10.5 wydaje się), musisz użyć:@Rule public MockitoJUnitRule mockito = new MockitoJUnitRule(this);
Cliff Sun
MockitoAnnotations.initMocks(this)bardzo wolno tworzy kpiny. Najbardziej wydajnym sposobem jest użycie @Runwith (MockitoJunitRunner.class)
ant2009
16

To rozwiązanie działa dla każdego możliwego biegacza, nie tylko ten przykład mockito. Na przykład; na wiosnę po prostu zmień klasy biegaczy i dodaj niezbędne adnotacje.

@RunWith(JUnitParamsRunner.class)
public class DatabaseModelTest {

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

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

DatabaseModelTestbędzie prowadzony przez JUnit. TestMockitoJUnitRunnerzależy od tego (zgodnie z logiką) i zostanie uruchomione wewnątrz metody main w @Testmetodzie podczas wywołania JUnitCore.runClasses(TestMockitoJUnitRunner.class). Ta metoda zapewnia, że ​​główny profil jest uruchamiany poprawnie przedstatic class TestMockitoJUnitRunner pomocniczego, skutecznie implementując wiele zagnieżdżonych @RunWithadnotacji z zależnymi klasami testowymi.

Również na https://bekce.github.io/junit-multiple-runwith-dependent-tests

bekce
źródło
3
Dzwoniąc JUnitCore.runClasses()bez sprawdzenia wyniku, ryzykujesz zamaskowanie błędów z testu wewnętrznego. assert(JUnitCore.runClasses(TestMockitoJUnitRunner.class).wasSuccessful());przynajmniej zgłosi błąd
Robotnik
2

W moim przypadku próbowałem Kpić z jakiejś metody w wiosennej fasoli i

MockitoAnnotations.initMocks(test);

nie działa. Zamiast tego musisz zdefiniować ten bean do konstruowania przy użyciu metody mock w pliku xml, jak poniżej.

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

i dodaj tę fasolę z autowiredem w klasie testowej, jak naśladowanie.

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

sprawdź ten link https://bekce.github.io/junit-multiple-runwith-dependent-tests/ stosując to podejście połączyłem @RunWith (Parameterized.class) - biegacz zewnętrzny - z @RunWith (MockitoJUnitRunner.class) - biegacz wewnętrzny. Jedyną poprawką, jaką musiałem dodać, było uczynienie moich zmiennych składowych w zewnętrznej klasie / runner statyczne, aby były dostępne dla wewnętrznej / zagnieżdżonej runner / klasy. mam szczęście i ciesz się.

Legna
źródło
0

Chciałem uruchomić SWTBotJunit4ClassRunner i org.junit.runners.Parametryzowane w tym samym czasie, mam testy parametryczne i chcę zrzuty ekranu, gdy test SWT się nie powiedzie (funkcję zrzutu ekranu zapewnia SWTBotJunit4ClassRunner ). Odpowiedź @ bekce jest świetna i najpierw chciałem iść tą drogą, ale albo było dziwaczne, gdy przechodziło przez argumenty. Lub zrobienie parametryzacji w podklasie i utrata informacji, jakie dokładnie testy przeszły / nie powiodły się i mają tylko ostatni zrzut ekranu (ponieważ nazwy zrzutów ekranu otrzymują nazwę z samego testu). Tak czy inaczej było trochę niechlujnie.

W moim przypadku SWTBotJunit4ClassRunner jest dość prosty, więc sklonowałem kod źródłowy klasy, nadałem mu własną nazwę ParametrizedScreenshotRunner i tam, gdzie oryginał rozszerzał TestRunner , moja klasa rozszerzyła parametr Parameterized klasę więc w zasadzie mogę użyć własnego runnera zamiast dwóch poprzednich. Podsumowując, mój własny runner rozszerza się na Sparametryzowany biegacz, jednocześnie implementując funkcję zrzutu ekranu, teraz mój test używa tego „hybrydowego” biegacza i wszystkie testy działają zgodnie z oczekiwaniami od razu (nie ma potrzeby niczego zmieniać w testach).

Tak to wygląda (ze względu na zwięzłość usunąłem wszystkie komentarze z aukcji):

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
źródło
-15

Możesz też spróbować tego:

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

@RunWith(MockitoJUnitRunner.class)
public class DatabaseModelTest extends AbstractTestClass {
  // some tests
}
Valentin Uveges
źródło
2
To nie zadziała, tylko adnotacja podklasy zostanie przetworzona.
PaulNUK
nie działa - tylko adnotacja MockitoJUnitRunner będzie brana pod uwagę
Przemek Bielicki