Paralleles Ausführen von Junit-Tests in einem Maven-Build?

110

Ich verwende JUnit 4.4 und Maven und habe eine große Anzahl lang laufender Integrationstests.

Wenn es um die Parallelisierung von Testsuiten geht, gibt es einige Lösungen, mit denen ich jede Testmethode in einer einzelnen Testklasse parallel ausführen kann. Aber all dies erfordert, dass ich die Tests auf die eine oder andere Weise ändere.

Ich denke wirklich, dass es eine viel sauberere Lösung wäre, X verschiedene Testklassen in X Threads parallel auszuführen. Ich habe Hunderte von Tests, daher ist es mir nicht wirklich wichtig, einzelne Testklassen einzufädeln.

Gibt es eine Möglichkeit, dies zu tun?

Krosenvold
quelle

Antworten:

75

Verwenden Sie das Maven-Plugin:

<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
quelle
12
<parallel> wird von todsicher unterstützt, wenn Sie Junit 4.7 oder höher verwenden. todsicherer Führer
jontejj
42

Ab Junit 4.7 ist es jetzt möglich, Tests ohne TestNG parallel auszuführen. Eigentlich ist es seit 4.6 möglich, aber in 4.7 werden eine Reihe von Korrekturen vorgenommen, die es zu einer praktikablen Option machen. Sie können auch parallele Tests mit der Feder durchführen, über die Sie hier lesen können

Krosenvold
quelle
1
Auf der verlinkten Seite heißt es: "Bei den meisten Dual-Core-Lösungen ist die Ausführung mit parallelen Threads derzeit nie schneller als die Ausführung ohne Threads." Ist das noch so?
Raedwald
2
Ich würde denken, dass Ihre Tests immer noch davon profitieren würden, wenn sie E / A-Vorgänge durchführen. Wenn Ihre Komponententests beispielsweise eher Integrationstests ähneln und auf die Datenbank zugreifen, sollten sie durch paralleles Ausführen beschleunigt werden.
Dave
@Raedwald Erwarten Sie nicht zu viel für die kurzen, nicht io-gebundenen Unit-Tests, versuche ich zu sagen. Neuere Versionen von todsicher sind auch besser / effizienter als die im Beitrag beschriebenen 2.5, sodass Sie möglicherweise etwas bessere Ergebnisse erzielen.
Krosenvold
3
Sie geben an, dass dies möglich ist, können aber einen Link zu einer Erklärung hinzufügen, wie? Ihr zweiter Link ist für "mit Frühling", was mich nicht interessiert.
Cory Kendall
@ Krosenvold Link? Ich habe Probleme, eine integrierte Lösung zu finden.
Ilan Biala
10

Inspiriert von JUnits experimentellem ParallelComputer- Läufer habe ich meine eigenen ParallelSuite- und ParallelParameterized- Läufer gebaut. Mit diesen Läufern können Testsuiten und parametrisierte Tests leicht parallelisiert werden.

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

Die Verwendung ist einfach. Ändern Sie einfach den @ RunWith- Annotationswert in eine dieser Parallel * -Klassen.

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

tempus-fugit bietet etwas ähnliches, überprüfen Sie die Dokumente für Details. Es basiert auf JUnit 4.7 und Sie markieren einfach Ihren Test mit @RunWith(ConcurrentTestRunner).

Prost

Toby
quelle
3

Sie können die Open Source-Bibliothek - Test Load Balancer - auschecken . Es macht genau das, wonach Sie fragen - führen Sie verschiedene Testklassen parallel durch. Dies wird auf der Ant-Junit-Ebene integriert, sodass Sie Ihre Tests ohnehin nicht ändern müssen. Ich bin einer der Autoren der Bibliothek.

Denken Sie auch daran, sie nicht in Threads auszuführen, da Sie möglicherweise eine Sandbox auf Prozessebene benötigen. Wenn Sie beispielsweise in Ihren Integrationstests auf eine Datenbank stoßen, möchten Sie nicht, dass ein Test fehlschlägt, da ein anderer Test einige Daten in einem anderen Thread hinzugefügt hat. In den meisten Fällen werden Tests nicht in diesem Sinne geschrieben.

Wie haben Sie dieses Problem bisher gelöst?

Pavan
quelle
2

TestNG kann das (dies war mein erster Reflex - dann habe ich gesehen, dass Sie bereits viele Testfälle haben).

Schauen Sie sich für JUnit Parallel-Junit an .

Philant
quelle
3
Leider ist dies nicht die Antwort auf die Frage, die ich stelle. Parallel-Junit wird nur innerhalb einer einzelnen Testklasse ausgeführt. TestNG läuft auch nur innerhalb einer einzelnen Klasse, und meine Tests sind keine TestNG-Tests.
Krosenvold
@PlatinumAzure: Ich habe den Link aktualisiert. Ich weiß nicht, wie dieses Projekt gepflegt wird. Kürzlich wurde eine weitere Frage gestellt, um die Ausführung von Junit-Tests auf mehrere Computer zu verteilen .
Philant
2

Sie können die Tests parallel mit dem von Junit selbst bereitgestellten ParallelComputer ausführen. Hier ist ein kleiner Ausschnitt, um Ihnen den Einstieg zu erleichtern.

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

Dies ist hilfreich, wenn Sie Tests mit Code ausführen müssen, da keine Abhängigkeiten von Maven oder anderen Build-Management-Tools bestehen.

Bitte beachten Sie, dass dadurch alle Testfälle parallel ausgeführt werden. Wenn Sie Abhängigkeiten zwischen verschiedenen Testfällen haben, kann dies zu Fehlalarmen führen. Sie sollten sowieso keine voneinander abhängigen Tests haben.

Ashwin Sadeep
quelle
0

Eine andere Wahl: Punner, ein neuer Parallel Junit Runner und Maven Plugin. Sie müssen Ihren Code nicht ändern, sondern in Ihre pom.xml kopieren:

<!-- 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 kann Testmethoden parallel ausführen, Testausgaben separat und sauber halten.

Punner reduziert Ihre MVN-Konsolenausgaben wie folgt:

[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 erzeugt todsichere kompatible Ausgaben. Sie können auch Rohprotokolldaten und einen Bericht im Markdown-Format aus dem Berichtsverzeichnis abrufen:

  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 ist mein persönliches Projekt. Ich habe Punner geschrieben, um die Unit-Test-Phase einiger anderer Projekte wie IPC-Framework, feinkörniges Sperren, Journalservice, verteilte Workflow-Engine usw. zu beschleunigen. Das hat mir viel Wartezeit gespart.

Punner unterstützt noch keine erweiterten Funktionen. Ich bin sehr froh, wenn Sie es versuchen und mir ein Feedback geben könnten.

Guilin Sun.
quelle
-3

Sie können Ihren Test in einer Minute in einen TestNg-Test ändern (Sie müssen nur die Importe ändern). TestNG ist der beste Test für parallele Tests.


quelle
-3

Sie können Gridgain ausprobieren , mit dem Sie Ihre Tests auf ein Rechenraster verteilen können.

Jan Kronquist
quelle
1
Ich habe die GridGain-Lösung ausprobiert und hatte zwei Hauptprobleme. Zunächst müssen Sie GridGain anweisen, alles, was GridGain auch verwendet, aus dem Klassenpfad Ihrer Grid-Task auszuschließen, z. B. Spring und viele Apache Commons-Inhalte. Zweitens funktioniert das Laden von Netzwerkklassen, obwohl es eine brillante Idee ist, nicht für Bibliotheken, die den Klassenpfad durchsuchen möchten, z. B. Spring
Graham Lea,