Simple Demo of Gradle Parallel Tests
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:
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:
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:
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:
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:
If I run these tests in parallel, the test report shows the time required for each, though not the total:
The command line output is a bit clearer:
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.