As some of you know, Gradle (the company) offers a course called Introduction to Gradle (the open source project) every other month*.

*I should note that as this post is published there are no new Intro classes on the schedule, but that should change very soon.

I teach that course, and have been doing so for several years now. In fact, I taught one last week, and this time I learned that something new about Gradle. I thought I understood it before, but apparently not, or at least it doesn’t work the way I thought it did. This blog post gives the details.

TL;DR: When you set maxParallelForks in Gradle to run tests, the test classes run in parallel, but the individual tests in a single class do not.

One of the best features in Gradle for JVM-related projects is its ability to run tests in parallel. As discussed in the Gradle documentation, this implemented by setting the maxParallelForks property inside a test block in the build.gradle file to a number other than 1.

Unfortunately, the documentation doesn’t say whether the individual tests run in parallel, or if only multiple test classes run in parallel, and it turns out I’ve been assuming the wrong one.

Here’s a trivial test class in JUnit 5 that runs four tests and includes some code to print out the time required to execute them individually and together:

Test class with four tests and timing information

The @Tag annotation at the top of the test class is a way you can, through Gradle, specify which JUnit tests to run. Here is the corresponding Gradle build file:

Gradle build file used to run JUnit 5 tests in parallel

The useJUnitPlatform call tells Gradle these are JUnit 5 tests, and the includeTags property inside it is set to the value of the tagName field. That field is inside a corresponding gradle.properties file in the root, and contains only tagName=individual. The maxParallelForks property is computed from the Java runtime. On my machine, the calculation returns 9, which is way more than I need.

Running this single test class which contains all four tests together, the result is:

Running four tests together in a single test class

Since each test sleeps for 1 second, the result takes four seconds plus whatever time Gradle needed to run. The test report shows the details a bit more explicitly:

Gradle test report output

The timing values were computed in the lifecycle methods annotated with @BeforeEach, @AfterEach, @BeforeAll, and @AfterAll.

The result shows that even though I’m running in parallel, the demo runs the four individual tests in the class consecutively. This can be shown even more dramatically using a bit of Ascii Cinema:

You can see the individual tests each taking a second to run, one by one.

As an alternative, I split the tests into four separate test classes, labeled AppTest1 through AppTest4, each containing one test, as in:

One of four individual test classes, each containing a single test

If I run these tests in parallel, the test report shows the time required for each, though not the total:

Four tests running in parallel

The command line output is a bit clearer:

Running the individual tests in parallel

The total is now just under 3 seconds, but the feel of it is much faster, as this Ascii Cinema demonstration shows:

So there you go. All four test classes run in parallel, but the individual tests in a class do not. I’ve been using this parallel test capability in Gradle for years, but always in a repository that has many tests in many test classes, and didn’t realize there was a distinction in how they were handled. Now I know, and hopefully you do to. 🙂

The corresponding code can be found on GitHub.

Leave a Reply

This site uses Akismet to reduce spam. Learn how your comment data is processed.