Spring Boot 4.0, released on the 20th of November 2025, brings Spring Framework 7 and introduces changes for our testing setup. Understanding these changes is crucial for maintaining a robust test suite as we upgrade our applications. This article explores the key testing-related updates for Spring Boot 4.0 and Spring Framework 7. This includes the new modular architecture, the improved test context management, and practical guidance for a smooth transition to this new major release.
Understanding the New Modular Design
Spring Boot 4.0 introduces a fundamental shift in how Spring Boot dependencies are organized. Instead of shipping a large spring-boot-autoconfigure jar, Spring Boot now provides smaller, focused modules.
For applications using Spring Boot Starter POMs, most dependencies have to be refined as functionality is now split across multiple (potentially new) modules. Furthermore, the test infrastructure has been modularized as well, requiring extra attention during the upgrade.
The new modular design harmonizes starter usage in two important ways:
- Most technologies covered by Spring Boot now have a dedicated starter
- Each starter has a corresponding test starter companion
This change affects how we import and use Spring Boot classes, both in production and for testing.
Consider the RestTemplate class, which previously was part of spring-boot-starter-web. The web starter is now renamed to spring-boot-starter-webmvc, and the autoconfiguration for the RestTemplate is part of a new modular starter.
Here’s how the starter structure has changed:
|
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 30 31 32 33 34 35 36 |
<!-- Setup for RestTemplate with Spring Boot < 4.0 --> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-test</artifactId> <scope>test</scope> </dependency> <!-- Changes to pom.xml with Spring Boot 4.0 --> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-webmvc</artifactId> </dependency> <!-- This starter bundles the RestTemplate and the RestClient --> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-webclient</artifactId> </dependency> <!-- The required test dependencies are also modularized --> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-webmvc-test</artifactId> <scope>test</scope> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-restclient-test</artifactId> <scope>test</scope> </dependency> |
The new modular dependencies also require some changes to the imports for various classes, as the package structure has changed.
The Spring Boot 4.0 Migration Guide provides detailed information about which dependencies require updates.
Bonus tip: Spring Boot 4.0 ships with so-called classic Spring Boot Starter modules. These classic modules help to transition more smoothly as they mimic the previous non-modular approach. This allows for a step-by-step migration approach.
Introducing the RestTestClient
Spring Boot 4.0 adds support for the newly introduced RestTestClient, providing a modern alternative to the soon-to-be-deprecated TestRestTemplate. The RestTestClient offers a fluent API for testing REST endpoints and integrates seamlessly with both MockMvc and live server instances.
When we use a regular @SpringBootTest or when @AutoConfigureMockMvc is present, we can autowire a RestTestClient that operates on the underlying MockMvc instance. The HTTP test clients are no longer auto-configured, and we have to enable the auto-configuration with @AutoConfigureRestTestClient explicitly.
For integration tests using @SpringBootTest with either a defined or random port, a RestTestClient can be injected to target the running server directly.
Here’s a practical example demonstrating the RestTestClient in action:
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 |
import org.junit.jupiter.api.Test; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.resttestclient.autoconfigure.AutoConfigureRestTestClient; import org.springframework.boot.test.context.SpringBootTest; import org.springframework.test.web.servlet.client.RestTestClient; @AutoConfigureRestTestClient @SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT) public class HealthCheckIT { @Autowired private RestTestClient restClient; @Test void shouldReturnSuccessfulHealthCheck() { restClient .get() .uri("/actuator/health") .exchange() .expectStatus() .is2xxSuccessful(); } } |
The RestTestClient provides a cleaner, more expressive API compared to TestRestTemplate. The fluent interface makes test assertions more readable and aligns with modern testing practices.
As the TestRestTemplate moves toward deprecation, migrating to RestTestClient prepares our test suite for future Spring Boot releases.
TestContext Caching Now Pauses Cached Context
One of the most impactful improvements in Spring Framework 7 is the automatic pausing of cached application contexts. The Spring TestContext Framework has long featured context caching, which reuses already-started application contexts to drastically reduce build times. However, before Spring Framework 7, cached contexts remained fully active, with all their threads running.
This meant that multiple scheduled jobs, message queue listeners, and other background processes could run simultaneously across different cached contexts, potentially interfering with shared resources like databases or external services.
Spring Framework 7 addresses this issue by pausing contexts when they’re not actively in use and automatically restarting them when needed. All components implementing Lifecycle or SmartLifecycle enter a stopped state until the context is retrieved from the cache again. Components can opt out of pausing by returning false from SmartLifecycle#isPauseable().
We can observe this behavior with a scheduled task:
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 |
@Component public class ScheduledLogger { private static final Logger LOG = LoggerFactory.getLogger(ScheduledLogger.class); private final ApplicationContext applicationContext; public ScheduledLogger(ApplicationContext applicationContext) { this.applicationContext = applicationContext; } @Scheduled(fixedDelay = 100L) public void log() { LOG.info("Scheduled task executed within context '{}'.", applicationContext.hashCode()); } @EventListener(ContextPausedEvent.class) public void contextPaused(ContextPausedEvent event) { LOG.info("Application context '{}' has been paused.", event.getApplicationContext().hashCode()); } } |
Before Spring Framework 7, we would see log entries from multiple contexts running simultaneously, potentially causing resource conflicts. With context pausing, background processes remain stopped until their context becomes active again, eliminating interference between cached contexts.
|
1 2 3 4 5 6 |
INFO 17036 --- [scheduling-1] d.p.demo.service.ScheduledLogger : Scheduled task executed within context '989033734'. INFO 17036 --- [scheduling-1] d.p.demo.service.ScheduledLogger : Scheduled task executed within context '1350143289'. INFO 17036 --- [scheduling-1] d.p.demo.service.ScheduledLogger : Scheduled task executed within context '989033734'. INFO 17036 --- [scheduling-1] d.p.demo.service.ScheduledLogger : Scheduled task executed within context '1350143289'. INFO 17036 --- [scheduling-1] d.p.demo.service.ScheduledLogger : Scheduled task executed within context '989033734'. INFO 17036 --- [scheduling-1] d.p.demo.service.ScheduledLogger : Scheduled task executed within context '1350143289'. |
This enhancement ensures that background processes within cached contexts don’t consume resources unnecessarily. For applications with extensive integration test suites using context caching effectively, this change can significantly reduce memory usage and improve overall test suite performance.
The Spring Test Context Caching article provides additional strategies for optimizing build times through proper context management. Also, consider the Spring Test Profiler to make the most of the context caching.
Migrating to JUnit 6
JUnit 6 reached General Availability in September 2025 and serves as the foundation for Spring Boot 4.0 testing.
The good news: migrating from JUnit 5.x to JUnit 6.0 is considerably easier than the JUnit 4 to JUnit 5 transition. Only APIs deprecated for over two years have been removed, making JUnit 6.0 a drop-in replacement in most cases.
JUnit 6 brings several noteworthy improvements:
Starting with version 6.0, JUnit adopts JSpecify for null safety annotations, improving type safety and IDE support. The framework also provides enhanced support for @Nested test classes, enabling better test organization and more expressive test hierarchies.
However, JUnit 4 is now officially in maintenance mode, and Spring Framework 7.0 deprecates JUnit 4 support in favor of SpringExtension and JUnit Jupiter. When using the SpringRunner, we’ll now see a deprecation warning directing us toward the modern alternatives.
The JUnit Vintage test engine, which provided backward compatibility for JUnit 4 tests, is now deprecated. It reports an INFO level discovery issue when it finds JUnit 4 test classes, clarifying that the engine should only be used temporarily during migration to JUnit Jupiter or another testing framework with native JUnit Platform support.
The JUnit 6 migration guide offers comprehensive upgrade instructions for teams still maintaining JUnit 5-based test suites.
Adopting Testcontainers 2.0
Testcontainers 2.0 brings breaking changes that affect how we declare and use container dependencies. The most significant change: all modules now use the testcontainers- prefix, and container classes have been relocated to module-specific packages.
For example, the PostgreSQL container dependency changes from:
|
1 2 3 4 5 |
<dependency> <groupId>org.testcontainers</groupId> <artifactId>postgresql</artifactId> <scope>test</scope> </dependency> |
To the new format:
|
1 2 3 4 5 |
<dependency> <groupId>org.testcontainers</groupId> <artifactId>testcontainers-postgresql</artifactId> <scope>test</scope> </dependency> |
Testcontainers 2.0 also removes JUnit 4 support entirely, aligning with the broader ecosystem move toward JUnit 5 and JUnit 6. Spring Boot 4.0 enhances Testcontainers integration by adding @ServiceConnection support for MongoDB’s MongoDBAtlasLocalContainer, simplifying local development and testing with MongoDB Atlas.
Additional Testing Improvements
Spring Boot 4.0 introduces several smaller but valuable testing enhancements that improve our daily development experience.
Bean Overrides for Non-Singleton Beans
Previously, bean override annotations like @MockitoBean, @MockitoSpyBean, and @TestBean only worked with singleton beans.
Spring Framework 7 removes this limitation, allowing us to override prototype and custom-scoped beans:
|
1 2 3 4 5 6 7 8 |
@Component @Scope("prototype") public class OrderProcessor { public String processOrder() { return "Processing order"; } } |
We can now mock this prototype-scoped bean in our tests:
|
1 2 3 4 5 6 7 8 9 10 11 12 |
@SpringBootTest public class OrderServiceTest { @MockitoBean private OrderProcessor orderProcessor; @Test void shouldProcessOrder() { given(orderProcessor.processOrder()).willReturn("Order processed"); // Test implementation } } |
Before Spring Framework 7, attempting to override non-singleton beans resulted in an IllegalStateException. This enhancement provides greater flexibility when testing applications with complex bean scoping strategies.
Updated Testing Library Versions
Spring Boot 4.0 updates several core testing libraries to their latest versions:
- Mockito 5.20, bringing improved mocking capabilities
- HtmlUnit 4.17
- Awaitility 4.3.0
- AssertJ 3.27.6
- Hamcrest 3.0
- Selenium 4.37
These updates ensure we have access to the latest testing features and bug fixes across the ecosystem.
SpringExtension Context Scope Changes
The SpringExtension, which integrates the Spring TestContext Framework into JUnit Jupiter, now uses a test-method scoped ExtensionContext. While this change enables consistent dependency injection semantics within @Nested test class hierarchies, it may affect custom TestExecutionListener implementations.
If we discover that Spring-related integration tests in @Nested test class hierarchies fail after upgrading to Spring Framework 7.0, we can annotate the top-level class with @SpringExtensionConfig(useTestClassScopedExtensionContext = true) to restore the previous behavior.
Custom TestExecutionListener implementations that override prepareTestInstance(TestContext) may require modifications. Instead of calling testContext.getTestClass(), we should use testContext.getTestInstance().getClass() to retrieve the current test class.
Summary
Spring Boot 4.0 introduces changes to the testing setup, requiring initial setup work but delivers many testing-related benefits. The new modular design brings a more fine-grained structure for dependencies.
In short, here’s what’s new for testing with Spring Boot 4 and Spring Framework 7:
- The
RestTestClientprovides a modern alternative toTestRestTemplate, offering a more expressive API for REST endpoint testing. - Test context pausing in Spring Framework 7 represents a significant improvement, eliminating resource conflicts from multiple cached contexts running simultaneously.
- The migration to JUnit 6 proves much simpler than previous major version transitions, with most applications experiencing a smooth upgrade path.
- Testcontainers 2.0 requires updating module names changes, but the changes are straightforward and well-documented.
- Additional improvements like bean overrides for non-singleton beans and updated testing library versions enhance our testing capabilities.
The Spring Boot 4.0 Migration Guide and OpenRewrite recipes provide additional automation support for the upgrade process.
For non-testing-related changes for Spring Boot 4.0, check out the official Spring Boot 4.0 release notes.
If you’re looking for a single place to learn the ins and outs of automated testing to become a more productive and confident Spring Boot developer, consider enrolling your team in the Testing Spring Boot Applications Masterclass.
Joyful testing,
Philip
