26

I recently stumbled upon a simple way to parallelize the execute of tests via jUnit by specifying the following in a java project's pom.xml file:

<plugin>
 <groupId>org.apache.maven.plugins</groupId>
 <artifactId>maven-surefire-plugin</artifactId>
 <configuration>
  <parallel>classes</parallel>
 </configuration>
</plugin>

I've discovered that there are 2 test-classes (let's call them "badtestclass1" and "badtestclass2") that keep getting penalized by this parallel execution due to the way the tests in them have been written. Ideally, I would refactor those test-classes to behave better, but in the interim, I was wondering if there's a nifty way to "exclude" these specific classes from being executed in parallel. Basically, is there a way to execute everything else in parallel and then these 2 in sequence (or the other order, doesn't matter). Would something like the following work?

<plugin>
 <groupId>org.apache.maven.plugins</groupId>
 <artifactId>maven-surefire-plugin</artifactId>
 <configuration>
  <parallel>classes</parallel>
  <excludes>
   <excludesFile>badtestclass1</excludesFile>
   <excludesFile>badtestclass2</excludesFile>
  </excludes>
 </configuration>
</plugin>
BSJ
  • 1,185
  • 2
  • 10
  • 15

3 Answers3

21

@polaretto's answer suggests the jcip @NotThreadSafe annotation, which works with the surefire plugin in a build, but does not work with command line mvn test. @patson-luk's answer is on the right track, unfortunately by excluding the "bad test" in the default configuration, it remained excluded and was not run in the separate <execution>.

I managed to get this working using the following configuration:

For JUnit 5, these are sufficient

In my src/test/resources/junit-platform.properties file (or you can pass as command line parameters):

junit.jupiter.execution.parallel.enabled = true
junit.jupiter.execution.parallel.mode.default = concurrent

At the top of my not-thread-safe class (instead of the jcip annotation):

import static org.junit.jupiter.api.parallel.ExecutionMode.SAME_THREAD;

@Execution(SAME_THREAD)
class SingleThreadedTest {
   // ...
}

For JUnit 4, modify the accepted answer as follows:

<plugin>
  <groupId>org.apache.maven.plugins</groupId>
  <artifactId>maven-surefire-plugin</artifactId>
  <version>${maven-surefire-plugin.version}</version>
  <configuration>
    <!-- Skip default, define executions separately below -->
    <skip>true</skip>
  </configuration>
  <executions>
    <execution>
      <id>single-thread-test</id>
      <phase>test</phase>
      <goals>
        <goal>test</goal>
      </goals>
      <configuration>
        <!-- Not thread safe, run separately -->
        <includes>
          <include>**/SingleThreadedTest.java</include>
        </includes>
        <forkCount>1</forkCount>
        <reuseForks>false</reuseForks>
        <threadCount>1</threadCount>
        <skip>false</skip>
      </configuration>
    </execution>
    <execution>
      <id>multi-thread-test</id>
      <phase>test</phase>
      <goals>
        <goal>test</goal>
      </goals>
      <configuration>
        <excludes>
          <exclude>**/SingleThreadedTest.java</exclude>
        </excludes>
        <forkCount>4</forkCount>
        <reuseForks>true</reuseForks>
        <parallel>all</parallel>
        <useUnlimitedThreads>true</useUnlimitedThreads>
        <skip>false</skip>
      </configuration>
    </execution>
  </executions>
</plugin>
Daniel Widdis
  • 8,424
  • 13
  • 41
  • 63
  • Thank you for sharing! I am using JUnit 5, and should I placed `junit-platform.properties` inside `test/main/resources` or `src/main/resources`, somehow it doesn't seem to work with PowermockRunner.. – Drex Aug 24 '21 at 20:13
  • 1
    @KevD. I had them in `src/test/resources`, sorry for the typo. Should work anywhere on your classpath, though. I have since migrated to using the `junit-platform-maven-plugin` which takes these as parameters ([See here](https://github.com/oshi/oshi/blob/f5a111a19d84877093ea0468e292bb8142fd520e/pom.xml#L268)). – Daniel Widdis Aug 24 '21 at 20:24
15

You can annotate the classes you don't want parallelized with jcip @NotThreadSafe and leave the surefire configuration as it was in your starting example. This way, whenever surefire finds an annotated class it just executes it in a single thread. It's explained right here in the "Parallel Test Execution and Single Thread Execution" paragraph.

polaretto
  • 791
  • 9
  • 11
7

Exclude those 2 tests in the original test phrase and then create a new execution with those 2 classes running in single thread? :)

<plugin>
    <groupId>org.apache.maven.plugins</groupId>
    <artifactId>maven-surefire-plugin</artifactId>
    <configuration>
        <excludes>
            <exclude>path/to/your/class/badtestclass1.java</exclude>
            <exclude>path/to/your/class/badtestclass2.java</exclude>
        </excludes>
        <parallel>classes</parallel>
    </configuration>

    <executions>
        <execution>
            <id>single-thread-test</id>
            <phase>test</phase>
            <goals>
                <goal>test</goal>
            </goals>
            <configuration>
                <includes>
                    <include>path/to/your/class/badtestclass1.java</include>
                    <include>path/to/your/class/badtestclass2.java</include>
                </includes>
                <threadCount>1</threadCount>
            </configuration>
        </execution>
    </executions>
  </plugin>
Patson Luk
  • 128
  • 5
  • Ah, I was missing the path to my test class and the additional execution to not skip the 2 classes. Thank you kind sir. – BSJ Jul 17 '14 at 20:24
  • Interesting. When I copy this snippet the test badtestclass1 and 2 get excluded from the normal run however they are not run automatically serially when I run "mvn test". Is there an extra call I need to make? – Kannan Ekanath Jun 15 '17 at 13:37
  • 1
    @KannanEkanath a little late to the party here. The excludes in the default config prevent them from being included separately in the second execution. To get this working I have to skip the default configuration and have two executions. – Daniel Widdis Nov 01 '20 at 03:06