Esecuzione di test junit in parallelo in una build Maven?


110

Sto usando JUnit 4.4 e Maven e ho un gran numero di test di integrazione di lunga durata.

Quando si tratta di parallelizzare le suite di test, ci sono alcune soluzioni che mi consentono di eseguire ogni metodo di test in una singola classe di test in parallelo. Ma tutto ciò richiede che io modifichi i test in un modo o nell'altro.

Penso davvero che sarebbe una soluzione molto più pulita eseguire X diverse classi di test in X thread in parallelo. Ho centinaia di test quindi non mi interessa davvero creare thread di classi di test individuali.

C'è un modo per fare questo?

Risposte:


75

Usa il 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>

12
<parallel> è effettivamente supportato da surefire se stai usando Junit 4.7 o successivo. guida infallibile
jontejj

42

Da junit 4.7 è ora possibile eseguire test in parallelo senza utilizzare TestNG. In realtà è stato possibile dalla 4.6, ma ci sono una serie di correzioni apportate nella 4.7 che lo renderanno un'opzione praticabile. Puoi anche eseguire test paralleli con la molla, di cui puoi leggere qui


1
La pagina collegata dice "per la maggior parte delle soluzioni dual-core, l'esecuzione con thread paralleli non è attualmente mai più veloce dell'esecuzione senza thread". È ancora così?
Raedwald

2
Penso che se i tuoi test eseguissero qualsiasi IO ne trarrebbero comunque beneficio. Ad esempio, se gli unit test sono più simili a test di integrazione e raggiungono il database, l'esecuzione in parallelo dovrebbe velocizzarli.
Dave

@Raedwald Non aspettarti troppo per i brevi unit test non legati a io, è quello che sto cercando di dire. Le versioni più recenti di surefire sono anche migliori / più efficienti della 2.5 descritta nel post, quindi potresti ottenere risultati leggermente migliori.
krosenvold

3
Dici che è possibile, ma puoi includere un link a una spiegazione di come? Il tuo secondo link è per "with spring", che non mi interessa.
Cory Kendall

@krosenvold link? Sto lottando per trovare una soluzione integrata.
Ilan Biala

10

Ispirato dal runner sperimentale ParallelComputer di JUnit, ho costruito i miei runner ParallelSuite e ParallelParameterized . Utilizzando questi runner è possibile parallelizzare facilmente suite di test e test parametrizzati.

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'utilizzo è semplice. Basta cambiare il valore delle annotazioni @RunWith in una di queste classi Parallel * .

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

5

tempus-fugit offre qualcosa di simile, controlla i documenti per i dettagli. Si basa su JUnit 4.7 e devi solo contrassegnare il tuo test con @RunWith(ConcurrentTestRunner).

Saluti


3

Puoi controllare la libreria open source - Test Load Balancer . Fa esattamente quello che chiedi: esegui diverse classi di test in parallelo. Questo si integra a livello di ant-junit in modo da non dover modificare comunque i test. Sono uno degli autori della biblioteca.

Inoltre, pensa a non eseguirli nei thread poiché potresti aver bisogno di una sandbox a livello di processo. Ad esempio, se stai colpendo un DB nei tuoi test di integrazione, non vuoi che un test fallisca perché un altro test ha aggiunto alcuni dati in un thread diverso. La maggior parte delle volte, i test non sono scritti con questo in mente.

Infine, come hai risolto questo problema fino ad ora?


2

TestNG può farlo (questo è stato il mio primo riflesso - poi ho visto che hai già molti casi di prova).

Per JUnit, guarda parallel-junit .


3
Purtroppo questa non è la risposta alla domanda che mi pongo. parallel-junit viene eseguito solo all'interno di una singola classe di test. TestNG viene eseguito solo all'interno di una singola classe ei miei test non sono test TestNG.
krosenvold

@PlatinumAzure: ho aggiornato il link. Non so come sia mantenuto questo progetto. Un'altra domanda è stata posta recentemente per distribuire l'esecuzione di test junit su più macchine .
filant

2

È possibile eseguire i test in parallelo utilizzando ParallelComputer fornito da Junit stesso. Ecco un piccolo snippet per iniziare.

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

Ciò aiuterà quando è necessario eseguire test dal codice poiché non ha dipendenze da Maven o da altri strumenti di gestione della build.

Tieni presente che questo eseguirà tutti i casi di test in parallelo, se hai delle dipendenze tra diversi casi di test potrebbe causare falsi positivi. NON DOVRESTE comunque avere test interdipendenti.


0

Un'altra scelta: Punner, un nuovo runner parallelo di junit e plug-in di maven. Non devi cambiare il tuo codice, copialo nel tuo 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 può eseguire metodi di test in parallelo, può mantenere gli output dei test separati e puliti.

Punner ridurrà gli output della tua console mvn, in questo modo:

[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 produce output compatibili infallibili, puoi anche ottenere dati di log grezzi e un rapporto in formato markdown dalla directory dei rapporti:

  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 è il mio progetto personale, ho scritto Punner per accelerare la fase di test unitario di alcuni altri progetti come il framework IPC, il blocco a grana fine, il servizio di journal, il motore del flusso di lavoro distribuito, ecc. Mi ha risparmiato molto tempo di attesa.

Punner non supporta ancora alcune funzionalità avanzate. Sono molto contento che tu possa provarlo e darmi un feedback.


-3

Puoi cambiare il tuo test in TestNg test in un minuto (devi solo cambiare le importazioni), TestNG è il migliore nei test paralleli.


-3

Potresti provare Gridgain che ti consente di eseguire la distribuzione dei tuoi test su una griglia di calcolo.


1
Ho provato la soluzione GridGain e ho riscontrato due problemi principali. In primo luogo, devi dire a GridGain di escludere dal classpath del tuo task grid tutto ciò che utilizza anche GridGain, ad esempio Spring e molte cose di Apache Commons. In secondo luogo, il caricamento di classi di rete, sebbene sia un'idea geniale, non funziona per le biblioteche che desiderano cercare il percorso di classe, ad esempio Spring
Graham Lea
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.