Executando testes junit em paralelo em uma compilação Maven?

110

Estou usando JUnit 4.4 e Maven e tenho um grande número de testes de integração de longa duração.

Quando se trata de paralelizar suítes de teste, existem algumas soluções que me permitem executar cada método de teste em uma única classe de teste em paralelo. Mas tudo isso exige que eu altere os testes de uma forma ou de outra.

Eu realmente acho que seria uma solução muito mais limpa executar X classes de teste diferentes em X threads em paralelo. Eu tenho centenas de testes, então eu realmente não me importo em encadear classes de teste individuais.

Há alguma maneira de fazer isso?

Krosenvold
fonte

Respostas:

75

Use o 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
fonte
12
<parallel> na verdade é suportado pelo surefire se você estiver usando o Junit 4.7 ou posterior. guia infalível
jontejj
42

A partir do junit 4.7, agora é possível executar testes em paralelo sem usar TestNG. Na verdade, isso é possível desde o 4.6, mas há uma série de correções sendo feitas no 4.7 que o tornarão uma opção viável. Você também pode executar testes paralelos com a mola, sobre os quais pode ler aqui

Krosenvold
fonte
1
A página vinculada diz "para a maioria das soluções de núcleo duplo, executar com threads paralelas nunca é mais rápido do que executar sem threads". Esse ainda é o caso?
Raedwald,
2
Eu pensaria que se seus testes fizessem qualquer IO, eles ainda se beneficiariam. Por exemplo, se seus testes de unidade são mais como testes de integração e atingem o banco de dados, a execução em paralelo deve acelerá-los.
Dave
@Raedwald Não espere muito muito para os testes de unidade não-io-encadernados curtas é o que estou tentando dizer. As versões mais recentes do surefire também são melhores / mais eficientes do que a 2.5 descrita no post, então você pode obter resultados ligeiramente melhores.
krosenvold
3
Você afirma que é possível, mas pode incluir um link para uma explicação de como? Seu segundo link é para "com primavera", no qual não estou interessado.
Cory Kendall
link @krosenvold? Estou lutando para encontrar uma solução integrada.
Ilan Biala
10

Inspirado por experimental do JUnit ParallelComputer corredor Eu construí meus próprios ParallelSuite e ParallelParameterized corredores. Usando esses executores, é possível paralelizar facilmente suítes de teste e testes parametrizados.

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

O uso é simples. Basta alterar o valor das anotações @RunWith para uma dessas classes Parallel * .

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

tempus-fugit oferece algo semelhante, verifique a documentação para obter detalhes. Ele depende do JUnit 4.7 e você apenas marca seu teste como @RunWith(ConcurrentTestRunner).

Felicidades

Toby
fonte
3

Você pode verificar a biblioteca de código aberto - Test Load Balancer . Ele faz exatamente o que você pede - executa diferentes classes de teste em paralelo. Isso se integra no nível ant-junit para que você não precise alterar seus testes de forma alguma. Eu sou um dos autores da biblioteca.

Além disso, pense em não executá-los em threads, pois você pode precisar de uma sandbox de nível de processo. Por exemplo, se você estiver atingindo um banco de dados em seus testes de integração, não deseja que um teste falhe porque outro teste adicionou alguns dados em um thread diferente. Na maioria das vezes, os testes não são escritos com isso em mente.

Enfim, como resolvemos esse problema até agora?

Pavan
fonte
2

TestNG pode fazer isso (este foi meu primeiro reflexo - então eu vi que você já estava tendo muitos casos de teste).

Para JUnit, olhe paralelo-junit .

filante
fonte
3
Infelizmente, essa não é a resposta à pergunta que estou fazendo. paralelo-junit é executado apenas em uma única classe de teste. TestNG também só é executado em uma única classe e meus testes não são testes TestNG.
krosenvold
@PlatinumAzure: Eu atualizei o link. Não sei como esse projeto é mantido. Outra pergunta foi feita recentemente para distribuir a execução de testes junit em várias máquinas .
filante
2

Você pode executar os testes em paralelo usando o ParallelComputer fornecido pela própria Junit. Aqui está um pequeno trecho para você começar.

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

Isso ajudará quando você precisar executar testes a partir do código, pois ele não depende do Maven ou de qualquer outra ferramenta de gerenciamento de construção.

Observe que isso executará todos os casos de teste em paralelo, se você tiver alguma dependência entre diferentes casos de teste, isso pode resultar em falsos positivos. Você NÃO DEVE ter testes interdependentes de qualquer maneira.

Ashwin Sadeep
fonte
0

Outra opção: Punner, um novo corredor paralelo junit e plug-in maven. Você não precisa alterar seu código, copie-o para o seu 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 pode executar métodos de teste em paralelo, pode manter as saídas de teste separadas e limpas.

Punner reduzirá as saídas do console mvn, como este:

[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.

O Punner produz resultados compatíveis com a certeza de que você também pode obter dados de registro brutos e um relatório de formato de markdown do diretório de relatórios:

  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 é meu projeto pessoal, escrevi Punner para acelerar a fase de teste de unidade de alguns outros projetos, como estrutura IPC, bloqueio refinado, serviço de diário, mecanismo de fluxo de trabalho distribuído, etc. Economizou muito do meu tempo de espera.

O Punner ainda não oferece suporte a algum recurso avançado. Fico muito feliz se você puder experimentar e me dar alguns comentários.

Guilin Sun
fonte
-3

Você pode alterar o seu teste para ser um teste TestNg em um minuto (você só precisa alterar as importações), TestNG é o melhor em testes paralelos.


fonte
-3

Você pode tentar o Gridgain, que permite que você execute e distribua seus testes em uma grade de computação.

Jan Kronquist
fonte
1
Experimentei a solução GridGain e tive dois problemas principais. Em primeiro lugar, você tem que dizer ao GridGain para excluir do classpath de sua tarefa de grade qualquer coisa que GridGain também usa, por exemplo, Spring e muitas coisas do Apache Commons. Em segundo lugar, o carregamento de classe de rede, embora seja uma ideia brilhante, não funciona para bibliotecas que desejam pesquisar o caminho de classe, por exemplo, Spring
Graham Lea