java junit automated-tests parallel-processing suite

java - Ejecutar JUnit Test en paralelo en Suite Level?



automated-tests parallel-processing (2)

Tengo un montón de pruebas que se organizan en suites de prueba JUnit. Estas pruebas utilizan mucho selenio para probar una aplicación web. Entonces, naturalmente para el selenio, el tiempo de ejecución de estas pruebas es bastante largo. Dado que las clases de prueba en las suites no pueden ejecutarse en paralelo debido a algunas superposiciones en la base de datos de prueba, me gustaría ejecutar las suites en paralelo.

El JUnit ParallelComputer solo puede ejecutar pruebas en nivel de clase o método en paralelo, ¿hay alguna forma estándar para que JUnit lo haga con suites?

Si solo paso las clases de suite al corredor de junit y configuro la computadora para que se paralelice en el nivel de clase, escoge las clases de prueba, no las suites.

Frank Frank


Como Suite se usa para anotar una Clase, ejecute la clase anotada en JUnitCore.runClasses(ParallelComputer.classes(), cls) en modo JUnitCore.runClasses(ParallelComputer.classes(), cls) . cls son clases anotadas en Suite.

@RunWith(Suite.class) @Suite.SuiteClasses({ Test1.class, Test2.class}) public class Suite1 { } @RunWith(Suite.class) @Suite.SuiteClasses({ Test3.class, Test4.class}) public class Suite2 { } ... JUnitCore.runClasses(ParallelComputer.classes(), new Class[]{Suite1.class, Suite2.class})


Aquí hay un código que funcionó para mí. Yo no escribí esto. Si usa @RunWith(ConcurrentSuite.class) lugar de @RunWith(Suite.class) debería funcionar. Hay una anotación que también se necesita que se encuentra a continuación.

package utilities.runners; import org.junit.internal.builders.AllDefaultPossibilitiesBuilder; import org.junit.runner.Runner; import org.junit.runners.Suite; import org.junit.runners.model.InitializationError; import org.junit.runners.model.RunnerBuilder; import org.junit.runners.model.RunnerScheduler; import utilities.annotations.Concurrent; import java.util.Arrays; import java.util.LinkedList; import java.util.List; import java.util.Queue; import java.util.concurrent.CompletionService; import java.util.concurrent.ExecutorCompletionService; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; import java.util.concurrent.Future; import java.util.concurrent.ThreadFactory; import java.util.concurrent.atomic.AtomicInteger; /** * @author Mathieu Carbou ([email protected]) */ public final class ConcurrentSuite extends Suite { public ConcurrentSuite(final Class<?> klass) throws InitializationError { super(klass, new AllDefaultPossibilitiesBuilder(true) { @Override public Runner runnerForClass(Class<?> testClass) throws Throwable { List<RunnerBuilder> builders = Arrays.asList( new RunnerBuilder() { @Override public Runner runnerForClass(Class<?> testClass) throws Throwable { Concurrent annotation = testClass.getAnnotation(Concurrent.class); if (annotation != null) return new ConcurrentJunitRunner(testClass); return null; } }, ignoredBuilder(), annotatedBuilder(), suiteMethodBuilder(), junit3Builder(), junit4Builder()); for (RunnerBuilder each : builders) { Runner runner = each.safeRunnerForClass(testClass); if (runner != null) return runner; } return null; } }); setScheduler(new RunnerScheduler() { ExecutorService executorService = Executors.newFixedThreadPool( klass.isAnnotationPresent(Concurrent.class) ? klass.getAnnotation(Concurrent.class).threads() : (int) (Runtime.getRuntime().availableProcessors() * 1.5), new NamedThreadFactory(klass.getSimpleName())); CompletionService<Void> completionService = new ExecutorCompletionService<Void>(executorService); Queue<Future<Void>> tasks = new LinkedList<Future<Void>>(); @Override public void schedule(Runnable childStatement) { tasks.offer(completionService.submit(childStatement, null)); } @Override public void finished() { try { while (!tasks.isEmpty()) tasks.remove(completionService.take()); } catch (InterruptedException e) { Thread.currentThread().interrupt(); } finally { while (!tasks.isEmpty()) tasks.poll().cancel(true); executorService.shutdownNow(); } } }); } static final class NamedThreadFactory implements ThreadFactory { static final AtomicInteger poolNumber = new AtomicInteger(1); final AtomicInteger threadNumber = new AtomicInteger(1); final ThreadGroup group; NamedThreadFactory(String poolName) { group = new ThreadGroup(poolName + "-" + poolNumber.getAndIncrement()); } @Override public Thread newThread(Runnable r) { return new Thread(group, r, group.getName() + "-thread-" + threadNumber.getAndIncrement(), 0); } } }

Y la anotación es la siguiente.

package utilities.annotations; import java.lang.annotation.ElementType; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; /** * @author Mathieu Carbou ([email protected]) */ @Retention(RetentionPolicy.RUNTIME) @Target({ ElementType.TYPE }) public @interface Concurrent { int threads() default 5; }