Exécuter des tests junit en parallèle dans une version Maven?

110

J'utilise JUnit 4.4 et Maven et j'ai un grand nombre de tests d'intégration de longue durée.

En ce qui concerne la parallélisation des suites de tests, il existe quelques solutions qui me permettent d'exécuter chaque méthode de test dans une seule classe de test en parallèle. Mais tout cela nécessite que je change les tests d'une manière ou d'une autre.

Je pense vraiment que ce serait une solution beaucoup plus propre d'exécuter X classes de test différentes dans des threads X en parallèle. J'ai des centaines de tests, donc je ne me soucie pas vraiment de threading des classes de test individuelles.

Y a-t-il un moyen de faire ça?

Krosenvold
la source

Réponses:

75

Utilisez le plugin maven:

<build>
    <plugins>
    <plugin>
        <groupId>org.apache.maven.plugins</groupId>
        <artifactId>maven-surefire-plugin</artifactId>
        <version>2.7.1</version>
        <configuration>
            <parallel>classes</parallel>
            <threadCount>5</threadCount>
        </configuration>
    </plugin>
    </plugins>
</build>
Oleksandr
la source
12
<parallel> est en fait pris en charge par surefire si vous utilisez Junit 4.7 ou version ultérieure. guide surefire
jontejj
42

Depuis junit 4.7, il est désormais possible d'exécuter des tests en parallèle sans utiliser TestNG. En fait, cela est possible depuis la version 4.6, mais un certain nombre de corrections apportées à la version 4.7 en feront une option viable. Vous pouvez également exécuter des tests parallèles avec spring, que vous pouvez lire ici

Krosenvold
la source
1
La page liée indique que "pour la plupart des solutions dual-core, l'exécution avec des threads parallèles n'est actuellement jamais plus rapide que l'exécution sans thread". Est-ce toujours le cas?
Raedwald le
2
Je pense que si vos tests faisaient des E / S, ils en bénéficieraient toujours. Par exemple, si vos tests unitaires ressemblent plus à des tests d'intégration et atteignent la base de données, l'exécution en parallèle devrait les accélérer.
Dave
@Raedwald N'en attendez pas trop pour les courts tests unitaires non liés à io, c'est ce que j'essaie de dire. Les nouvelles versions de surefire sont également meilleures / plus efficaces que la 2.5 décrite dans l'article, vous pouvez donc obtenir des résultats légèrement meilleurs.
krosenvold
3
Vous dites que c'est possible, mais pouvez-vous inclure un lien vers une explication de comment? Votre deuxième lien est pour "avec le printemps", ce qui ne m'intéresse pas.
Cory Kendall
Lien @krosenvold? J'ai du mal à trouver une solution intégrée.
Ilan Biala
10

Inspiré par le coureur expérimental ParallelComputer de JUnit, j'ai construit mes propres coureurs ParallelSuite et ParallelParameterized . En utilisant ces coureurs, on peut facilement paralléliser des suites de tests et des tests paramétrés.

ParallelSuite.java

public class ParallelSuite extends Suite {

    public ParallelSuite(Class<?> klass, RunnerBuilder builder) throws InitializationError {

        super(klass, builder);

        setScheduler(new RunnerScheduler() {

            private final ExecutorService service = Executors.newFixedThreadPool(4);

            public void schedule(Runnable childStatement) {
                service.submit(childStatement);
            }

            public void finished() {
                try {
                    service.shutdown();
                    service.awaitTermination(Long.MAX_VALUE, TimeUnit.NANOSECONDS);
                } catch (InterruptedException e) {
                    e.printStackTrace(System.err);
                }
            }
        });
    }
}

ParallelParameterized.java

public class ParallelParameterized extends Parameterized {

    public ParallelParameterized(Class<?> arg0) throws Throwable {

        super(arg0);

        setScheduler(new RunnerScheduler() {

            private final ExecutorService service = Executors.newFixedThreadPool(8);

            public void schedule(Runnable childStatement) {
                service.submit(childStatement);
            }

            public void finished() {
                try {
                    service.shutdown();
                    service.awaitTermination(Long.MAX_VALUE, TimeUnit.NANOSECONDS);
                } catch (InterruptedException e) {
                    e.printStackTrace(System.err);
                }
            }
        });
    }
}

L'utilisation est simple. Il suffit de changer @RunWith la valeur de l'un de ces parallèles * classes.

@RunWith(ParallelSuite.class)
@SuiteClasses({ATest.class, BTest.class, CTest.class})
public class ABCSuite {}
Mustafa Ulu
la source
5

tempus-fugit propose quelque chose de similaire, consultez la documentation pour plus de détails. Il repose sur JUnit 4.7 et vous marquez simplement votre test sur @RunWith(ConcurrentTestRunner).

À votre santé

Toby
la source
3

Vous pouvez consulter la bibliothèque open source - Test Load Balancer . Il fait exactement ce que vous demandez - exécutez différentes classes de test en parallèle. Cela s'intègre au niveau ant-junit afin que vous n'ayez pas à modifier vos tests de toute façon. Je suis l'un des auteurs de la bibliothèque.

Pensez également à ne pas les exécuter dans des threads car vous pourriez avoir besoin d'un bac à sable au niveau du processus. Par exemple, si vous atteignez une base de données dans vos tests d'intégration, vous ne voulez pas qu'un test échoue car un autre test a ajouté des données dans un thread différent. La plupart du temps, les tests ne sont pas écrits dans cet esprit.

Enfin, comment avez-vous résolu ce problème jusqu'à présent?

Pavan
la source
2

TestNG peut le faire (c'était mon premier réflexe - puis j'ai vu que vous aviez déjà beaucoup de tests).

Pour JUnit, regardez parallel-junit .

philant
la source
3
Malheureusement, ce n’est pas la réponse à la question que je pose. parallel-junit ne s'exécute que dans une seule classe de test. TestNG ne fonctionne également que dans une seule classe, et mes tests ne sont pas des tests TestNG.
krosenvold
@PlatinumAzure: J'ai mis à jour le lien. Je ne sais pas comment ce projet est maintenu. Une autre question a été posée récemment pour distribuer l'exécution des tests junit sur plusieurs machines .
philant
2

Vous pouvez exécuter les tests en parallèle à l'aide de ParallelComputer fourni par Junit lui-même. Voici un petit extrait pour vous aider à démarrer.

Class[] cls = { TestCase1.class, TestCase2.class };
Result result = JUnitCore.runClasses(ParallelComputer.classes(), cls);
List<Failure> failures = result.getFailures();

Cela vous aidera lorsque vous aurez besoin d'exécuter des tests à partir du code car il n'a aucune dépendance sur Maven ou tout autre outil de gestion de build.

Veuillez noter que cela exécutera tous les cas de test en parallèle, si vous avez des dépendances entre différents cas de test, cela peut entraîner des faux positifs. Vous NE DEVRIEZ PAS avoir de tests interdépendants de toute façon.

Ashwin Sadeep
la source
0

Autre choix: Punner, un nouveau plugin junit runner et maven parallèle. Vous n'avez pas besoin de changer votre code, copiez-le dans votre pom.xml:

<!-- Disable default surefire based testing -->
<plugin>
  <groupId>org.apache.maven.plugins</groupId>
  <artifactId>maven-surefire-plugin</artifactId>
  <version>2.20</version>
  <configuration>
    <skip>true</skip>
  </configuration>
</plugin>

<plugin>
  <groupId>com.github.marks-yag</groupId>
  <artifactId>punner-maven-plugin</artifactId>
  <version>${version}</version>
  <configuration>
  </configuration>
  <executions>
    <execution>
      <id>test</id>
      <phase>test</phase>
      <goals>
        <goal>test</goal>
      </goals>
    </execution>
  </executions>
</plugin>

Punner peut exécuter des méthodes de test en parallèle, peut conserver les sorties de test séparément et nettoyer.

Punner réduira les sorties de votre console mvn, comme ceci:

[INFO] --- punner-maven-plugin:0.9.13:test (test) @ ipc ---
[INFO] Punner report directory: /Users/guile/workspace/ipc/target/punner-reports
[INFO]
[INFO] com.github.yag.ipc.IPCTest.testConnectionHandler.............. PASSED
[INFO] com.github.yag.ipc.IPCTest.testSequence....................... PASSED
[INFO] com.github.yag.ipc.IPCTest.testPartialContent................. PASSED
[INFO] com.github.yag.ipc.IPCTest.testResponseContent................ PASSED
[INFO] com.github.yag.ipc.IPCTest.testPingPong....................... PASSED
[INFO] com.github.yag.ipc.IPCTest.testServerClose.................... PASSED
[INFO] com.github.yag.ipc.IPCTest.testServerSideHeartbeatTimeout..... PASSED
[INFO] com.github.yag.ipc.IPCTest.testClientSideHeartbeatTimeout..... PASSED
[INFO] com.github.yag.ipc.IPCTest.testClientSideHeartbeat............ PASSED
[INFO] com.github.yag.ipc.IPCTest.testClientReconnect................ PASSED
[INFO]
[INFO] Tests run: 10, Failures: 0, Errors: 0, Skipped: 0, Time elapsed: 10.952 sec, Time saved: 25.919 sec.

Punner produit des sorties compatibles infaillibles, vous pouvez également obtenir des données de journal brutes et un rapport au format de démarque à partir du répertoire des rapports:

  ipc git:(develop) ll target/punner-reports
total 104
-rw-r--r--   1 guile  staff    11K Oct 15 23:07 TEST-com.github.yag.ipc.IPCTest.xml
-rw-r--r--   1 guile  staff   298B Oct 15 23:07 com.github.yag.ipc.IPCTest.txt
drwxr-xr-x  12 guile  staff   384B Oct  8 00:50 logs
-rw-r--r--   1 guile  staff    33K Oct 15 23:07 report.md

Punner est mon projet personnel, j'ai écrit Punner pour accélérer la phase de test unitaire de certains autres projets tels que le framework IPC, le verrouillage fin, le service de journal, le moteur de flux de travail distribué, etc. Cela a économisé beaucoup de temps d'attente.

Punner ne prend pas encore en charge certaines fonctionnalités avancées. Je suis très heureux si vous pouviez l'essayer et me faire part de vos commentaires.

Guilin Sun
la source
-3

Vous pouvez changer votre test en test TestNg en une minute (il vous suffit de changer les importations), TestNG est le meilleur des tests en parallèle.


la source
-3

Vous pouvez essayer Gridgain qui vous permet d'exécuter la distribution de vos tests sur une grille de calcul.

Jan Kronquist
la source
1
J'ai essayé la solution GridGain et j'ai eu deux problèmes majeurs. Tout d'abord, vous devez dire à GridGain d'exclure du chemin de classe de votre tâche de grille tout ce que GridGain utilise également, par exemple Spring et beaucoup de choses Apache Commons. Deuxièmement, le chargement de classe réseau, bien qu'il soit une idée brillante, ne fonctionne pas pour les bibliothèques qui souhaitent rechercher le chemin de classe, par exemple Spring
Graham Lea