Question Test JUnit simultané


J'ai une grande suite de tests JUnit, où je voudrais bien exécuter tous les tests simultanément pour deux raisons:

  • Exploitez plusieurs cœurs pour exécuter la suite de tests plus rapidement
  • Espérons détecter certaines erreurs dues à des objets globaux non-thread-safe

Je reconnais que cela va me forcer à refactoriser du code pour le rendre sûr du thread, mais je considère que c'est une bonne chose :-)

Quel est le meilleur moyen pour que JUnit exécute tous les tests simultanément?


59
2018-02-11 15:35


origine


Réponses:


Êtes-vous fixé sur JUnit? TestNG fournit de bons tests multi-threads et est compatible avec les tests JUnit (vous devez apporter quelques modifications). Par exemple, vous pouvez exécuter un test comme celui-ci:

@Test(threadPoolSize = 3, invocationCount = 9,  timeOut = 10000)
public void doSomething() {
...
}

Cela voudrait dire que le doSomething() La méthode sera invoquée 9 fois par 3 threads différents.

Je recommande fortement TestNG.


23
2018-04-24 13:11



Je cherchais une réponse exacte à cette question et, sur la base des réponses fournies ici et de ce que je lis ailleurs, il semble qu’il n’existe pas actuellement de méthode simple pour exécuter des tests existants en parallèle. JUnit. Ou s'il y a je ne l'ai pas trouvé. J'ai donc écrit un simple JUnit Runner qui accomplit cela. N'hésitez pas à l'utiliser. voir http://falutin.net/2012/12/30/multithreaded-testing-with-junit/ pour une explication complète et le code source de la classe MultiThreadedRunner. Avec cette classe, vous pouvez simplement annoter vos classes de test existantes comme ceci:

@RunWith(MultiThreadedRunner.class)

20
2018-01-01 03:57



Le code suivant doit répondre à vos exigences qui a été tiré du livre allemand JUnit Profiwissen qui contient des astuces pour tester des choses en parallèle ou pour réduire le temps d'exécution en utilisant plusieurs cœurs au lieu d'un seul cœur.

JUnit 4.6 a introduit une classe ParallelComputer qui offre l'exécution en parallèle des tests. Cependant, cette fonctionnalité n’était pas accessible publiquement jusqu’à JUnit 4.7, qui offrait la possibilité de définir un planificateur personnalisé pour le coureur parent.

public class ParallelScheduler implements RunnerScheduler {

    private ExecutorService threadPool = Executors.newFixedThreadPool(
        Runtime.getRuntime().availableProcessors());

    @Override
    public void schedule(Runnable childStatement) {
        threadPool.submit(childStatement);
    }

    @Override
    public void finished() {
        try {
            threadPool.shutdown();
            threadPool.awaitTermination(Long.MAX_VALUE, TimeUnit.NANOSECONDS);
        } catch (InterruptedException e) {
            Thread.currentThread().interrupt();
            throw new RuntimeException("Got interrupted", e);
        }
    }
}

public class ParallelRunner extends BlockJUnit4ClassRunner {

    public ParallelRunner(Class<?> klass) throws InitializationError {
        super(klass);
        setScheduler(new ParallelScheduler());
    }
}

Si vous annotez maintenant une classe de test avec @RunWith(ParallelRunner.class) chaque méthode s'exécutera dans son propre thread. De plus, il y aura autant de threads actifs (uniquement) que de processeurs disponibles sur la machine d’exécution.

Si plusieurs classes doivent être exécutées en parallèle, vous pouvez définir une suite personnalisée comme ceci:

public class ParallelSuite extends Suite {

    public ParallelSuite(Class<?> klass, RunnerBuilder builder) 
      throws InitializationError {
        super(klass, builder);
        setScheduler(new ParallelScheduler());
    }
}

et ensuite changer @RunWith(Suite.class) avec @RunWith(ParallelSuite.class)

Vous pouvez même exploiter les fonctionnalités de f.e. la WildcardPatternSuite en étendant directement à partir de cette suite au lieu de Suite comme dans l'exemple précédent. Cela vous permet en outre de filtrer les tests unitaires f.e. par n'importe quel @Category - un TestSuite qui n'exécute que UnitTest les catégories annotées en parallèle pourraient ressembler à ceci:

public interface UnitTest {

}

@RunWith(ParallelSuite.class)
@SuiteClasses("**/*Test.class")
@IncludeCategories(UnitTest.class)
public class UnitTestSuite {

}

Un simple cas de test pourrait maintenant ressembler à ceci:

@Category(UnitTest.class)
@RunWith(MockitoJUnitRunner.class)
public class SomeClassTest {

    @Test
    public void testSomething() {
        ...
    }
}

le UnitTestSuite va exécuter chaque classe trouvée dans les sous-répertoires qui se termine par Test et a un @Category(UnitTest.class) spécifié en parallèle - en fonction du nombre de cœurs de CPU disponibles.

Je ne sais pas si cela peut être plus simple que ça :)


18
2017-10-07 10:52



Apparemment, Mathieu Carbou a mis en œuvre pour la concurrence qui pourrait aider!

http://java.dzone.com/articles/concurrent-junit-tests

OneJunit

@RunWith(ConcurrentJunitRunner.class)
@Concurrent(threads = 6)
public final class ATest {

    @Test public void test0() throws Throwable { printAndWait(); }
    @Test public void test1() throws Throwable { printAndWait(); }
    @Test public void test2() throws Throwable { printAndWait(); }
    @Test public void test3() throws Throwable { printAndWait(); }
    @Test public void test4() throws Throwable { printAndWait(); }
    @Test public void test5() throws Throwable { printAndWait(); }
    @Test public void test6() throws Throwable { printAndWait(); }
    @Test public void test7() throws Throwable { printAndWait(); }
    @Test public void test8() throws Throwable { printAndWait(); }
    @Test public void test9() throws Throwable { printAndWait(); }

    void printAndWait() throws Throwable {
        int w = new Random().nextInt(1000);
        System.out.println(String.format("[%s] %s %s %s",Thread.currentThread().getName(), getClass().getName(), new Throwable       ().getStackTrace()[1].getMethodName(), w));
        Thread.sleep(w);
    }
}

Plusieurs unités J:

@RunWith(ConcurrentSuite.class)
@Suite.SuiteClasses({ATest.class, ATest2.class, ATest3.class})
public class MySuite {
}

4
2017-07-22 18:36



Vous pouvez aussi essayer HavaRunner. C'est un runner JUnit qui exécute des tests en parallèle par défaut.

HavaRunner a aussi des suites pratiques: vous pouvez déclarer un test comme membre d'une suite en ajoutant l'annotation @PartOf(YourIntegrationTestSuite.class) sur la classe. Cette approche diffère de celle de JUnit, où vous déclarez les appartenances à la suite dans la classe de la suite.

De plus, les suites HavaRunner peuvent intialiser des objets lourds tels qu'un conteneur d'application Web intégré. HavaRunner transmet ensuite cet objet lourd au constructeur de chaque membre de la suite. Cela supprime le besoin de @BeforeClass et @AfterClass les annotations, qui posent problème, car elles favorisent un état mutable global, ce qui rend la parallélisation difficile.

Enfin, HavaRunner propose des scénarios - une manière d’exécuter le même test avec des données différentes. Les scénarios réduisent la nécessité de dupliquer le code de test.

HavaRunner a été testé dans deux projets Java de taille moyenne.

Ps. Je suis l'auteur de HavaRunner, et j'apprécierais vos commentaires à ce sujet.


2
2017-12-08 11:02