I'm trying to run tests in parallel, but with a fixed number of threads.
- maven-failsafe-plugin 2.22.2
- JUnit 5.9.1
- Selenium 4.7.0
- JDK8
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-failsafe-plugin</artifactId>
<version>2.22.2</version>
<!-- Parallel configuration see junit-platform.properties -->
</plugin>
If I start off with junit-platform.properties setting
junit.jupiter.execution.parallel.enabled=false
the tests run single-threaded, as expected.
Setting
junit.jupiter.execution.parallel.enabled=true
junit.jupiter.execution.parallel.mode.default=same_thread
junit.jupiter.execution.parallel.mode.classes.default=concurrent
junit.jupiter.execution.parallel.config.strategy=fixed
junit.jupiter.execution.parallel.config.fixed.parallelism=2
results in ca. 10 browsers starting in parallel. This shows that maven-failsafe-plugin honors the junit-platform.properties, but not the fixed strategy.
JUnit issue 'Parallelism value ignored for the fixed strategy' #2273 describes this issue, and more specifically Selenium issue 'Can no longer limit the number of parallel sessions from JUnit 5 #9359' describes the impact on Selenium versions 4.0.0-alpha-4 and up.
However, implementing a custom parallel strategy as described in the above issues and setting
junit.jupiter.execution.parallel.enabled=true
junit.jupiter.execution.parallel.mode.default=same_thread
junit.jupiter.execution.parallel.mode.classes.default=concurrent
junit.jupiter.execution.parallel.config.strategy=custom
junit.jupiter.execution.parallel.config.custom.class=ch.want.funnel.integration.base.CustomParallelStrategy
doesn't change anything, there are still ca. 10 browsers opening in parallel.
I can't find an SO post nor a github issue describing this behavior, which is that maven-failsafe-plugin does honor junit-platform.properties, does honor the junit.jupiter.execution.parallel.enabled
setting therein, but not the parallel.config.strategy.
Update Dec 16th 2022
Looking at ForkJoinPoolHierarchicalTestExecutorService, the ForkJoinPool is created with
private ForkJoinPool createForkJoinPool(ParallelExecutionConfiguration configuration) {
ForkJoinWorkerThreadFactory threadFactory = new WorkerThreadFactory();
return Try.call(() -> {
// Try to use constructor available in Java >= 9
Constructor<ForkJoinPool> constructor = ForkJoinPool.class.getDeclaredConstructor(Integer.TYPE,
ForkJoinWorkerThreadFactory.class, UncaughtExceptionHandler.class, Boolean.TYPE, Integer.TYPE,
Integer.TYPE, Integer.TYPE, Predicate.class, Long.TYPE, TimeUnit.class);
return constructor.newInstance(configuration.getParallelism(), threadFactory, null, false,
configuration.getCorePoolSize(), configuration.getMaxPoolSize(), configuration.getMinimumRunnable(),
configuration.getSaturatePredicate(), configuration.getKeepAliveSeconds(), TimeUnit.SECONDS);
}).orElseTry(() -> {
// Fallback for Java 8
return new ForkJoinPool(configuration.getParallelism(), threadFactory, null, false);
}).getOrThrow(cause -> new JUnitException("Failed to create ForkJoinPool", cause));
}
so the configuration maxPoolSize is only taken into account for JDK9 and higher. Also, Eclipse calls
ForkJoinPoolHierarchicalTestExecutorService#invokeAll(List<? extends TestTask>)
individually for each test class, while Maven calls once with all test classes.