· 3 min read

Speeding Up Spring Boot/JUnit tests

From 8 minutes to less than 3 minutes.

From 8 minutes to less than 3 minutes.
For beginners: Tests provide fast feedback to your code and ensure everything works as you intended. Rapid tests help get feedback and ensure your integration and deployment pipelines work faster.

There are several ways I recently discovered that increase speed on Spring Boot + JUnit tests substantially. From 8 minutes to 3 minutes.

Some of these are

  1. Use Right Test Slice (Saved 25% running time)
  2. Speeding up @SpringBootTest and avoiding @DirtiesContext (Saved 25% running time)
  3. Run tests in parallel (Saved 37% running time)

Use Right Test Slice (Saved 25% running time)

Several runners come with Spring Boot.

@SpringBootTest is the most general of them. Primarily this is used in combination with @DirtiesContent, which will restart spring boot after every test class by default which isn’t ideal.

Using @SpringBootTest is Integration Tests. We don’t want every test to use this.

We want to load the minimum spring-boot infrastructure as possible and still ensure accurate tests. There are a few things that can help:

  1. If tests are related to a DB. Use @DataMongoTest or equivalent JPA runner with @Import annotation.
  2. If tests are related to Web/Controller. Use @WebFluxTest or equivalent Spring MVC runner. If you want to use embedded MongoDB with it, use @AutoConfigureDataMongo. Most other systems also have annotations starting with @AutoConfigure{systemName}
  3. Important: If tests require initializing a single bean. Use @ExtendWith(SpringRunner.class) with @Import to specify the bean to initialize.
  4. The best case: Aim for Tests that are purely java based and do not involve spring boot, but it isn’t possible in every scenario.

Speeding up @SpringBootTest and avoiding @DirtiesContext (Saved 25% running time)

@DirtiesContext is an annotation that recreated the Spring Context after each test or each test class. To avoid using @DirtiesContext, ensure all your tests don’t depend on the same data. For this, you can wrap your data creation in a test data factory and ensure the data produced is random.

As previously mentioned, one should avoid using @SpringBootTest, but if you can’t do without it, ensure you use WebEnvironment.MOCK without @DirtiesContext.

@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.MOCK)

Try to use it with @Import or @ContextConfiguration to limit the creation of beans.


Run tests in parallel (Saved 37% running time)

Create a junit-platform.properties file in test/resource/junit-platform.properties and add the following minimum. 1

junit.jupiter.execution.parallel.enabled = true

Now you can either use one of these,

  1. add the following to every class you want to run parallel. @Execution(CONCURRENT)
  2. add more to junit-platform.properties
junit.jupiter.execution.parallel.mode.default = same_thread|concurrent
junit.jupiter.execution.parallel.mode.classes.default =  same_thread|concurrent

1

Some tests can’t run parallelly, so I prefer manually adding @Execution(CONCURRENT).

I found data tests with @DataMongoTest by asserting over different data.

Another positive side effect of having @Execution(CONCURRENT) is that these tests run first. And since these tests are faster than the SAME_THREAD mode, they lead to instant feedback.


What are some other ways to speed up JUnit performance?

Footnotes

  1. Parallel Execution - JUnit Documentation 2


Back to Blog