Contents

Run JUnit5 Tests in Parallel

Written by: David Vlijmincx

Introduction

In this article, we look at how to execute tests in parallel using JUnit5. First, we will have to add one line of configuration to our project to enable the Parallel Execution feature. Then, after enabling Parallel Execution, we look at how to use this feature. The last example will be about synchronizing tests that share a common resource while still trying to run other tests in parallel.

Configuration

To enable parallel execution we need to create a file called junit-platform.properties inside the src/test/resources folder. Inside the file, you need to add the following line:

1
junit.jupiter.execution.parallel.enabled=true

The line above will enable the basic functionality and allow you to run tests in parallel. However, there a more settings to customize the parallel execution of unit tests.

customize the parallel execution of unit tests

If you want all your tests to run in parallel by default, you must add the following line:

1
junit.jupiter.execution.parallel.mode.default=concurrent

JUnit includes two strategies that allow you to change how many threads will be used to run tests in parallel.

  • dynamic: Computes the number of threads it can use. The default is 1. The default will use all the threads available. When you set it to .5, only half the amount of threads will be used.
  • fixed: the value will equal the number of threads used for running tests in parallel.

For a dynamic strategy, add the following lines:

1
2
junit.jupiter.execution.parallel.config.strategy=dynamic
junit.jupiter.execution.parallel.config.dynamic.factor=1

For a fixed strategy, add the following lines:

1
2
junit.jupiter.execution.parallel.config.strategy=fixed
junit.jupiter.execution.parallel.config.fixed.parallelism=6

Using @Execution(CONCURRENT)

If you only added junit.jupiter.execution.parallel.enabled=true to the junit-platform properties file and didn't change the default parallel mode. Then, you need to add the @Execution(CONCURRENT) annotation to the test class containing the unit tests you want to run in parallel.

In the following example, I have a test class annotated with @Execution(CONCURRENT) to run the unit tests in parallel. The total runtime of this class will now be 3 seconds instead of 6 seconds.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
import org.junit.jupiter.api.parallel.Execution;
import static org.junit.jupiter.api.parallel.ExecutionMode.CONCURRENT;

@Execution(CONCURRENT)
public class parallelTests {

    @Test
    void oneSecondTest() throws InterruptedException {
        Thread.sleep(1000);
    }

    @Test
    void twoSecondTest() throws InterruptedException {
        Thread.sleep(2000);
    }

    @Test
    void threeSecondTest() throws InterruptedException {
        Thread.sleep(3000);
    }
    
}

Using @ResourceLock

Unit tests shouldn't be dependent on each other. There are, however, test cases where unit tests share a recourse. For example, running tests in parallel that read and write to a shared resource can be flaky and unexpectedly fail. To prevent having test fails because they run in parallel, we use the @ResourceLock(value = "KeyName, mode = READ) annotation.

The mode inside the @ResourceLock tells JUnit what action the unit test performs on the shared resource. For example, READ actions can run in parallel, While READ_WRITE will run concurrently. The value is a key/name for the shared resource.

READ unit tests will run in parallel with other READ tests while READ_WRITE will run alone/in the same thread

In the following example, we have three tests that use a shared database resource with the key name “DB”. The tests annotated with @ResourceLock(value = "DB", mode = READ) will run in parallel. The method annotated with @ResourceLock(value = "DB", mode = READ_WRITE) will run in the same thread. The total runtime of this class is 4 seconds.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
import static org.junit.jupiter.api.parallel.ExecutionMode.CONCURRENT;
import static org.junit.jupiter.api.parallel.ResourceAccessMode.READ;
import static org.junit.jupiter.api.parallel.ResourceAccessMode.READ_WRITE;

@Execution(CONCURRENT)
class parallelTestsSync {

    @Test
    @ResourceLock(value = "DB", mode = READ_WRITE)
    void oneSecondTest() throws InterruptedException {
        Thread.sleep(1000);
        // read from database
    }

    @Test
    @ResourceLock(value = "DB", mode = READ)
    void twoSecondTest() throws InterruptedException {
        Thread.sleep(2000);
        // read from database
    }

    @Test
    @ResourceLock(value = "DB", mode = READ)
    void threeSecondTest() throws InterruptedException {
        Thread.sleep(3000);
        // read and write to database
    }
    
}

Conclusion

In this article, we looked at how to execute unit tests in parallel using JUnit5. First, we enabled the option to run tests in parallel. We covered different kinds of configurations that are available for parallel execution. We created tests that run in parallel and saw how to coordinate tests that share a resource. Please see the official documentation if you want to know more about JUnit5

Further reading

More about testing in Java: