¿Ejecutar pruebas junit en paralelo en una compilación de Maven?

110

Estoy usando JUnit 4.4 y Maven y tengo una gran cantidad de pruebas de integración de larga duración.

Cuando se trata de paralelizar conjuntos de pruebas, hay algunas soluciones que me permiten ejecutar cada método de prueba en una única clase de prueba en paralelo. Pero todos estos requieren que cambie las pruebas de una forma u otra.

Realmente creo que sería una solución mucho más limpia ejecutar X clases de prueba diferentes en X subprocesos en paralelo. Tengo cientos de pruebas, así que realmente no me importa enhebrar clases de prueba individuales.

¿Hay alguna forma de hacer esto?

krosenvold
fuente

Respuestas:

75

Utilice el complemento de 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
fuente
12
<parallel> es compatible con surefire si está usando Junit 4.7 o posterior. guía infalible
jontejj
42

Desde junit 4.7 ahora es posible ejecutar pruebas en paralelo sin usar TestNG. De hecho, ha sido posible desde 4.6, pero se están realizando varias correcciones en 4.7 que lo convertirán en una opción viable. También puede ejecutar pruebas paralelas con Spring, sobre las que puede leer aquí

krosenvold
fuente
1
La página vinculada dice "para la mayoría de las soluciones de doble núcleo, ejecutar con subprocesos paralelos actualmente nunca es más rápido que ejecutar sin subprocesos". ¿Sigue siendo el caso?
Raedwald
2
Creo que si sus pruebas hacen alguna IO, todavía se beneficiarían. Por ejemplo, si sus pruebas unitarias son más como pruebas de integración y llegan a la base de datos, ejecutarlas en paralelo debería acelerarlas.
Dave
@Raedwald No espere demasiado para las pruebas unitarias cortas no vinculadas a io, es lo que estoy tratando de decir. Las versiones más nuevas de surefire también son mejores / más eficientes que la 2.5 descrita en la publicación, por lo que puede obtener resultados ligeramente mejores.
krosenvold
3
Dice que es posible, pero ¿puede incluir un enlace a una explicación de cómo? Su segundo enlace es para "con primavera", que no me interesa.
Cory Kendall
@krosenvold enlace? Estoy luchando por encontrar una solución integrada.
Ilan Biala
10

Inspirado por el corredor ParallelComputer experimental de JUnit, he construido mis propios corredores ParallelSuite y ParallelParameterized . Con estos corredores, se pueden paralelizar fácilmente conjuntos de pruebas y pruebas parametrizadas.

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

El uso es simple. Simplemente cambie el valor de las anotaciones @RunWith a una de estas clases Parallel * .

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

tempus-fugit ofrece algo similar, consulte los documentos para obtener más detalles. Se basa en JUnit 4.7 y solo marca su prueba en @RunWith(ConcurrentTestRunner).

Salud

Toby
fuente
3

Puede consultar la biblioteca de código abierto: Test Load Balancer . Hace exactamente lo que pide: ejecutar diferentes clases de prueba en paralelo. Esto se integra en el nivel ant-junit para que no tenga que cambiar sus pruebas de ninguna manera. Soy uno de los autores de la biblioteca.

Además, piense en no ejecutarlos en subprocesos, ya que es posible que necesite una zona de pruebas a nivel de proceso. Por ejemplo, si está alcanzando una base de datos en sus pruebas de integración, no desea que una prueba falle porque otra prueba agregó algunos datos en un hilo diferente. La mayoría de las veces, las pruebas no se escriben teniendo esto en cuenta.

Finalmente, ¿cómo se ha resuelto este problema hasta ahora?

Pavana
fuente
2

TestNG puede hacer eso (este fue mi primer reflejo, luego vi que ya tienes muchos casos de prueba).

Para JUnit, busque en paralelo-junit .

filántropo
fuente
3
Desafortunadamente, esta no es la respuesta a la pregunta que hago. paralelo-junit solo se ejecuta dentro de una única clase de prueba. TestNG también solo se ejecuta dentro de una sola clase, y mis pruebas no son pruebas de TestNG.
krosenvold
@PlatinumAzure: Actualicé el enlace. No sé cómo se mantiene este proyecto. Recientemente se hizo otra pregunta para distribuir la ejecución de pruebas junit en varias máquinas .
philant
2

Puede ejecutar las pruebas en paralelo utilizando ParallelComputer proporcionado por el propio Junit. Aquí hay un pequeño fragmento para comenzar.

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

Esto ayudará cuando necesite ejecutar pruebas desde el código, ya que no depende de Maven ni de ninguna otra herramienta de administración de compilación.

Tenga en cuenta que esto ejecutará todos los casos de prueba en paralelo, si tiene alguna dependencia entre diferentes casos de prueba, podría dar como resultado falsos positivos. De todos modos NO DEBE tener pruebas interdependientes.

Ashwin Sadeep
fuente
0

Otra opción: Punner, un nuevo corredor junit paralelo y complemento maven. No tiene que cambiar su código, cópielo en su 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 puede ejecutar métodos de prueba en paralelo, puede mantener las salidas de prueba por separado y limpias.

Punner reducirá las salidas de su consola mvn, así:

[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 salidas compatibles infalibles, también puede obtener datos de registro sin procesar y un informe en formato de rebajas del directorio de informes:

  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 es mi proyecto personal, escribí Punner para acelerar la fase de prueba unitaria de algunos otros proyectos, como el marco de IPC, el bloqueo detallado, el servicio de diario, el motor de flujo de trabajo distribuido, etc. Me ahorró mucho tiempo de espera.

Punner aún no admite algunas funciones avanzadas. Me alegro mucho si pudieras probarlo y darme tu opinión.

Guilin Sun
fuente
-3

Puede cambiar su prueba para que sea TestNg en un minuto (solo necesita cambiar las importaciones), TestNG es el mejor en pruebas paralelas.


fuente
-3

Puede probar Gridgain, que le permite ejecutar y distribuir sus pruebas en una cuadrícula de cálculo.

Jan Kronquist
fuente
1
He probado la solución GridGain y he tenido dos problemas importantes. En primer lugar, debe decirle a GridGain que excluya de la ruta de clase de la tarea de la cuadrícula cualquier cosa que GridGain también use, por ejemplo, Spring y muchas cosas de Apache Commons. En segundo lugar, la carga de clases de la red, aunque es una idea brillante, no funciona para las bibliotecas que quieren buscar la ruta de clases, por ejemplo, Spring
Graham Lea