In my previous Spring Boot 4 article, I covered the main new features of Spring Boot 4 and how to migrate your application using OpenRewrite.
This second article focuses on the new test features and changes of Spring Boot 4. I will cover modularity, application context pausing, JUnit 6, Mockito and the new RestTestClient.
The articles in this series:
In the previous article, I explained that Spring Boot 4 is now more modular. This impacts the dependencies you bring
to the application. You now have to be more explicit. For example to implement web tests,
you need to add the spring-boot-starter-webmvc-test dependency:
<!-- To use @WebMvcTest, @AutoConfigureRestTestClient, etc. -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-webmvc-test</artifactId>
<scope>test</scope>
</dependency>
Some packages were also renamed. For example, org.springframework.boot.test.autoconfigure.web.servlet.WebMvcTest
is now org.springframework.boot.webmvc.test.autoconfigure.WebMvcTest.
Spring Boot 4 now uses Testcontainers 2. It changes some dependency names:
<dependency>
<groupId>org.testcontainers</groupId>
<artifactId>testcontainers-postgresql</artifactId><!-- Instead of just "postgresql" -->
<scope>test</scope>
</dependency>
For these types of breaking changes, you can use OpenRewrite to automatically update your code. See the previous article for more details about OpenRewrite and how to use it.
As I explained in Getting started with Spring tests, Spring caches the various application contexts created and used during the execution of the tests. This improves the tests performance (in terms of duration and memory usage) by reducing the number of application contexts. You still want to make sure that you do not use more application contexts than necessary, see the article for more details and tips about that.
Before Spring Framework 7, cached application contexts remain active. Their scheduled jobs, and JMS listeners still run, even when the context is not being used. This consumes resources but can also cause side effects and make the tests less reliable.
With Spring Framework 7, cached application contexts are automatically paused when not used and restarted when needed again. This improves the performances and reduces the side effects.
With the spring.test.context.cache.pause property, you can configure the pause mode
or even disable the feature if you need backward compatibility:
ON_CONTEXT_SWITCH : the default mode, an inactive context is paused only when the next one is different.ALWAYS: a context is paused whenever it becomes inactive, regardless of the next context.NEVER: the application contexts are not paused, choose this for backward compatibility.See Context Pausing for additional information.
Spring Framework 7 upgraded JUnit to version 6. This new major version brings many changes and improvements. But the migration impact for Spring application developers is less significant than from JUnit 4 to 5.
JUnit 6 supports JSpecify. It means that the public APIs of JUnit now use the JSpecify annotations to declare their nullability. See Null safety section of the previous article for more details about JSpecify and null safety.
I will not cover the new features of JUnit 6 in this article, for additional information see JUnit 6 Release Notes and the reference documentation.
Spring Boot has been supporting Mockito for a long time. Using @MockBean and @SpyBean you could easily create
Mockito mocks and spies in your Spring tests, and have them injected into the application context.
With Spring Boot 3.4, @MockBean and @SpyBean were deprecated in favor of two new annotations: @MockitoBean
and @MockitoSpyBean.
Now in Spring Boot 4, the old annotations have been removed and only the new ones are available.
Consider a @RestController HelloController that delegates to a @Service HelloService.
Here is a code sample showing how to use @MockitoBean in a web slice test with MockMvc
(spoiler alert, in the next section we will see a newer alternative to MockMvc):
import org.springframework.boot.webmvc.test.autoconfigure.WebMvcTest; // The package name changed in SB 4
import org.springframework.test.context.bean.override.mockito.MockitoBean;
// ... other imports ...
@WebMvcTest(HelloController.class)
class HelloControllerSliceTest {
@Autowired
private MockMvc mockMvc;
@MockitoBean // The new annotation
private HelloService helloService;
@Test
void greet() throws Exception {
Mockito.when(helloService.greet()).thenReturn("Hello");
mockMvc.perform(get("/hello"))
.andExpect(status().isOk())
.andExpect(content().string("Hello"));
}
}
@MockitoBean and @MockitoSpyBean (as well as @TestBean) now also work on non-singleton beans,
such as prototype or custom scoped beans.
Finally, to use the new annotations you do not have to rename manually, you can use OpenRewrite instead.
To test the REST API of your Spring Boot 3 applications, several options are available:
MockMvc, TestRestTemplate, WebTestClient, REST Assured, etc.
MockMvc can be used to call your controllers without an actual HTTP request being made. This is sometimes called
"server web testing", since the test code is executed on the server side, by the same thread as the business code.
It is also convenient to use for web slice tests , where you want to test only the web layer of your application,
without starting the whole application context.
The code sample from the previous section shows how to do that.
As explained in my Getting started with Spring tests article, you can also perform
"client web testing", where you start the server and call it with a real HTTP connection.
It means that the test method is executed by another thread than the controller.
For these tests, up to Spring Boot 3, I usually prefer REST Assured over TestRestTemplate but both are valid options.
As a consequence, depending on the nature of your tests (unit, web slice, client web, server web, etc.), you may have to use different web test clients.
These options still work in Spring Boot 4, but a new solution is now available starting from
Spring Framework 7: RestTestClient.
It uses a fluent API and is based on RestClient. But its main selling point is that it can be used in most
of your web test scenarios. This single tool can be used in unit tests, web slices, client web,
server web, etc.
Before describing RestTestClient usages, a note about REST Assured. It still works fine in Spring Boot 4,
but it is no longer directly supported since it is not present in the dependency management anymore.
This means that you have to add it as a dependency along with the chosen version.
To use RestTestClient in a client web test, for example as a replacement for REST Assured, you can do the following:
import org.springframework.test.web.servlet.client.RestTestClient;
// ... other imports ...
@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
@AutoConfigureRestTestClient
class HelloControllerClientTest {
@Autowired
private RestTestClient client;
@Test
void greet() {
client.get().uri("/hello").exchange()
.expectStatus().isOk()
.expectBody(String.class).isEqualTo("Hello");
}
}
Note that @AutoConfigureRestTestClient, similarly to @AutoConfigureMockMvc, can cause the creation of an
additional Spring application context.
To use RestTestClient in a server web test, for example as a replacement of MockMvc, simply remove
the webEnvironment from @SpringBootTest:
@SpringBootTest
@AutoConfigureRestTestClient
class HelloControllerServerTest {
// The rest is identical!
}
In the previous section, I showed how to implement a web slice test with MockMvc.
Here is the RestTestClient equivalent:
@WebMvcTest(HelloController.class)
@AutoConfigureRestTestClient
class HelloControllerSliceTest {
@Autowired
private RestTestClient client;
@MockitoBean
private HelloService helloService;
@Test
void greet() {
Mockito.when(helloService.greet()).thenReturn("Hello");
client.get().uri("/hello").exchange()
.expectStatus().isOk()
.expectBody(String.class).isEqualTo("Hello");
}
}
You can even use RestTestClient in a unit test, without any Spring application context:
class HelloControllerUnitTest {
private HelloService helloService;
private RestTestClient client;
@BeforeEach
void beforeEach() {
helloService = Mockito.mock(HelloService.class);
client = RestTestClient.bindToController(new HelloController(helloService)).build();
}
@Test
void greet() {
Mockito.when(helloService.greet()).thenReturn("Hello");
client.get().uri("/hello").exchange()
.expectStatus().isOk()
.expectBody(String.class).isEqualTo("Hello");
}
}
The previous examples show only GET requests and simple assertions,
but RestTestClient supports other HTTP methods and more advanced checks.
See RestTestClient documentation
or examples from my spring-tests sample repository.
In this article, I presented some of the main new test features of Spring Boot 4. I hope you enjoyed this selection.
If you want additional tips on how to get started with tests in your Spring Boot applications, feel free to read Getting started with Spring tests. I originally wrote that article for Spring Boot 3 but it is still relevant.
© 2007-2026 Florian Beaufumé