75 votes

Simultanées JUnit test

J'ai un grand JUnit test de suite, où que j'aimerais exécuter tous les tests en même temps pour deux raisons:

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

Je reconnais que cela va me forcer à revoir certaines de code pour le rendre thread-safe, mais je considère que pour être une bonne chose :-)

Quelle est la meilleure façon d'obtenir JUnit pour exécuter tous les tests en même temps?

29voto

Chandradeep Points 113

Êtes-vous fixé à JUnit? TestNG fournit un bon multi thread test de la boîte et il est compatible avec JUnit tests (vous avez besoin de faire quelques changements). Par exemple, vous pouvez exécuter un test comme ceci:

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

Cela signifierait que l' doSomething() méthode sera invoquée 9 fois par 3 différents threads.

Je recommande fortement TestNG.

24voto

Mike Sokolov Points 3441

J'ai été à la recherche pour répondre exactement à cette question, et sur la base des réponses ici, et ce que j'ai lu ailleurs, il semble comme si il n'y a actuellement pas facile de dehors-de-le-boîte de façon à exécuter des tests en parallèle en utilisant JUnit. Ou, si ce n'est que je n'avais pas à le trouver. J'ai donc écrit un simple Runner JUnit qui accomplit que. 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 MultiThreadedRunner classe. Avec cette classe, vous pouvez annoter votre classe de test(es) comme ceci:

@RunWith(MultiThreadedRunner.class)

22voto

Roman Vottner Points 1278

Le code suivant devrait atteindre vos exigences, qui a été prise à partir du livre allemand JUnit Profiwissen qui contient quelques conseils pour soit des tests, des trucs en parallèle ou sur la façon de réduire le temps d'exécution par le biais de l'utilisation de plusieurs cœurs au lieu d'un seul cœur.

JUnit 4.6 introduit un ParallelComputer classe qui a offert l'exécution en parallèle de tests. Toutefois, cette fonctionnalité n'était pas accessible au public jusqu'à ce que JUnit 4.7 qui donnent la possibilité de personnaliser le planificateur pour le parent coureur.

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

Maintenant, si vous annoter un test de classe avec @RunWith(ParallelRunner.class) chaque méthode fonctionne dans son propre thread. En outre, il y aura autant de threads actifs (seulement) comme CPU-cores sont disponibles à l'exécution de la machine.

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

public class ParallelSuite extends Suite {

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

et puis le changement @RunWith(Suite.class) avec @RunWith(ParallelSuite.class)

Vous pouvez même tirer parti de la fonctionnalité de f.e. l' WildcardPatternSuite en étendant directement à partir de la suite au lieu de Suite comme dans l'exemple de l'avant. Cela vous permet en outre à l'unité de filtre-tests f.e. par tout @Category - une suite de tests qui n'exécute UnitTest annoté catégories en parallèle, pourrait ressembler à ceci:

public interface UnitTest {

}

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

}

Un simple cas de test pourrait ressembler à ceci:

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

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

L' UnitTestSuite seront exécutées à chaque classe a trouvé en subdirecotries qui se termine par Test et a un @Category(UnitTest.class) spécifié en parallèle en fonction du nombre de cœurs de PROCESSEUR disponibles.

Je ne sais pas si il peut faire plus simple que ça :)

6voto

molavec Points 141

Apparemment Mathieu Carbou a fait implamentation à des fins d'approbation qui pourrait aide!

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 JUnits:

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

2voto

Lauri L Points 54

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

HavaRunner a également pratique suites: vous pouvez déclarer un test pour être membre d'une suite en ajoutant l'annotation @PartOf(YourIntegrationTestSuite.class) sur la classe. Cette approche diffère de JUnit, où vous déclarez la suite de l'adhésion à la suite de la classe.

En outre, HavaRunner suites peut initialiser les poids lourds des objets tels qu'une application web intégrée conteneur. HavaRunner transmet ensuite cette "poids lourds" de l'objet dans le constructeur de chaque suite membre. Cela supprime la nécessité pour l' @BeforeClass et @AfterClass des annotations, qui sont problématiques, car ils favorisent mondiale mutable état, ce qui rend la parallélisation difficile.

Enfin, HavaRunner a des scénarios – un moyen d'exécuter le même test sur des données différentes. Les scénarios de réduire le besoin de dupliquer le code de test.

HavaRunner a été testée en conditions réelles dans les deux mi-taille des projets Java.

Ps. Je suis l'auteur de HavaRunner, et j'apprécierais vos commentaires sur celui-ci.

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X