Plusieurs instructions RunWith dans jUnit

113

J'écris des tests unitaires et je veux utiliser JUnitParamsRunneret MockitoJUnitRunnerpour une classe de test.

Malheureusement, ce qui suit ne fonctionne pas:

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

Existe-t-il un moyen d'utiliser à la fois Mockito et JUnitParams dans une classe de test?

Hans-Helge
la source
2
Il y a un bel exemple ici aussi: blog.project13.pl/index.php/coding/1077/…
falsarella

Réponses:

110

Vous ne pouvez pas faire cela car, selon les spécifications, vous ne pouvez pas placer la même annotation deux fois sur le même élément annoté.

Alors, quelle est la solution? La solution est d'en mettre un seul @RunWith()avec un coureur dont vous ne pouvez pas vous passer et de remplacer un autre par autre chose. Dans votre cas, je suppose que vous supprimerez MockitoJUnitRunneret ferez par programme ce qu'il fait.

En fait, la seule chose qu'il fait, c'est s'exécuter:

MockitoAnnotations.initMocks(test);

au début du cas de test. Donc, la solution la plus simple est de mettre ce code en setUp()méthode:

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

Je ne suis pas sûr, mais vous devriez probablement éviter les appels multiples de cette méthode en utilisant flag:

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

Cependant, une meilleure solution réutilisable peut être implémentée avec les règles de JUnt.

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

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

Maintenant, ajoutez simplement la ligne suivante à votre classe de test:

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

et vous pouvez exécuter ce cas de test avec n'importe quel coureur de votre choix.

AlexR
la source
12
Le contrôle mockInitializedest faux. Vous voulez avoir une nouvelle maquette pour chaque tétst.
BetaRide
1
@BetaRide, cela dépend de vos besoins. Parfois, vous souhaitez initialiser la simulation à chaque fois, parfois non.
AlexR
Si vous souhaitez configurer une fois par fichier de classe, vous pouvez utiliser BeforeClass au lieu de Before, qui sera appelé une et une seule fois par fichier de test.
InfernalRapture
56

Depuis JUnit 4.7 et Mockito 1.10.17, cette fonctionnalité est intégrée; il y a une org.mockito.junit.MockitoRuleclasse. Vous pouvez simplement l'importer et ajouter la ligne

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

à votre classe de test.

Erica Kane
la source
2
Pour les anciennes versions de Mockito (jusqu'à 1.10.5 il semble), vous devez utiliser:@Rule public MockitoJUnitRule mockito = new MockitoJUnitRule(this);
Cliff Sun
MockitoAnnotations.initMocks(this)est très lent à créer des simulations. Le moyen le plus efficace est d'utiliser le @Runwith (MockitoJunitRunner.class)
ant2009
16

Cette solution fonctionne pour tous les coureurs possibles, pas seulement pour cet exemple de simulation. Par exemple; pour Spring, changez simplement les classes de coureurs et ajoutez les annotations nécessaires.

@RunWith(JUnitParamsRunner.class)
public class DatabaseModelTest {

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

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

DatabaseModelTestsera exécuté par JUnit. TestMockitoJUnitRunneren dépend (par logique) et il sera exécuté à l' intérieur du main dans une @Testméthode, lors de l'appel JUnitCore.runClasses(TestMockitoJUnitRunner.class). Cette méthode garantit que le programme d' static class TestMockitoJUnitRunnerexécution principal est démarré correctement avant l' exécution du sous-programme, implémentant efficacement plusieurs @RunWithannotations imbriquées avec des classes de test dépendantes.

Aussi sur https://bekce.github.io/junit-multiple-runwith-dependent-tests

bekce
la source
3
En appelant JUnitCore.runClasses()sans inspecter le résultat, vous risquez de masquer les erreurs du test interne. assert(JUnitCore.runClasses(TestMockitoJUnitRunner.class).wasSuccessful());vous signalera au moins l'erreur
Robotnik
2

Dans mon cas, j'essayais de simuler une méthode dans le haricot de printemps et

MockitoAnnotations.initMocks(test);

ne fonctionne pas. Au lieu de cela, vous devez définir ce bean comme étant construit en utilisant la méthode fictive dans votre fichier xml comme suit.

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

et ajoutez ce bean avec autowired dans votre classe de test comme suit.

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

consultez ce lien https://bekce.github.io/junit-multiple-runwith-dependent-tests/ en utilisant cette approche j'ai combiné un @RunWith (Parameterized.class) - coureur externe - avec @RunWith (MockitoJUnitRunner.class) - coureur intérieur. Le seul ajustement que j'ai dû ajouter était de rendre statiques mes variables membres dans la classe externe / le coureur afin de les rendre accessibles pour le coureur / la classe interne / imbriqué. porte chance et profite.

Legna
la source
0

Je voulais exécuter SWTBotJunit4ClassRunner et org.junit.runners.Parameterized en même temps, j'ai des tests paramétriques et je veux faire des captures d'écran lorsque le test SWT échoue (la fonction de capture d'écran est fournie par SWTBotJunit4ClassRunner ). La réponse de @ bekce est excellente et voulait d'abord emprunter cette voie, mais c'était soit excentrique en passant par les arguments. Ou faire le paramétré dans la sous-classe et perdre les informations sur les tests exacts qui ont réussi / échoué et n'ont que la dernière capture d'écran (car les noms de capture d'écran obtiennent le nom du test lui-même). De toute façon, c'était un peu compliqué.

Dans mon cas, le SWTBotJunit4ClassRunner est assez simple, donc j'ai cloné le code source de la classe, lui ai donné mon propre nom ParametrizedScreenshotRunner et là où l'original étendait le TestRunner , ma classe étend le Parameterized classe donc en substance je peux utiliser mon propre coureur au lieu des deux précédents. En résumé, mon propre coureur s'étend au-dessus du coureur paramétré tout en implémentant la fonction de capture d'écran par-dessus, maintenant mon test utilise ce coureur "hybride" et tous les tests fonctionnent immédiatement comme prévu (pas besoin de changer quoi que ce soit dans les tests).

Voici à quoi cela ressemble (par souci de concision, j'ai supprimé tous les commentaires de la liste):

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
la source
-15

Vous pouvez également essayer ceci:

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

@RunWith(MockitoJUnitRunner.class)
public class DatabaseModelTest extends AbstractTestClass {
  // some tests
}
Valentin Uveges
la source
2
Cela ne fonctionnera pas, seule l'annotation de sous-classe sera traitée.
PaulNUK
ne fonctionne pas - seule l'annotation MockitoJUnitRunner sera prise en compte
Przemek Bielicki