Advanced Testing Techniques for Spring Boot Applications
Elevate your Spring Boot testing strategy with mocking, slicing, integration testing, and testcontainers
Writing robust and maintainable Spring Boot applications requires more than just business logic — it demands thorough testing. While unit tests catch simple bugs, advanced systems need a more nuanced testing strategy to ensure reliability, performance, and integration under real conditions.
In this post, you’ll learn advanced testing techniques for Spring Boot applications using test slicing, mocking, integration testing, Testcontainers, and more — all tailored for modern, production-grade Java apps.
Test Pyramid for Spring Boot
Follow the test pyramid to balance speed and confidence:
- Unit Tests: Fast, isolated, test logic in classes
- Integration Tests: Validate system components together
- End-to-End (E2E): Full-stack tests mimicking real users
Spring Boot supports all of these layers through its rich testing ecosystem.
Setting Up Dependencies
Include JUnit 5, Mockito, and Spring Boot test modules:
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.testcontainers</groupId>
<artifactId>junit-jupiter</artifactId>
<version>1.19.0</version>
<scope>test</scope>
</dependency>
Also include specific Testcontainers modules for databases like PostgreSQL or MySQL as needed.
Layered Testing with @WebMvcTest and @DataJpaTest
Instead of loading the whole context, Spring provides test slices:
@WebMvcTest — for controller layer:
@WebMvcTest(UserController.class)
class UserControllerTest {
@Autowired
private MockMvc mockMvc;
@MockBean
private UserService userService;
@Test
void testGetUser() throws Exception {
when(userService.getUser(1L)).thenReturn(new UserDTO("John", "john@example.com"));
mockMvc.perform(get("/users/1"))
.andExpect(status().isOk())
.andExpect(jsonPath("$.name").value("John"));
}
}
@DataJpaTest — for repository layer:
@DataJpaTest
class UserRepositoryTest {
@Autowired
private UserRepository repository;
@Test
void testSaveUser() {
User user = new User("Alice", "alice@example.com");
User saved = repository.save(user);
assertNotNull(saved.getId());
}
}
Full Integration Tests with @SpringBootTest
For multi-layer testing, use @SpringBootTest
:
@SpringBootTest(webEnvironment = WebEnvironment.RANDOM_PORT)
class ApplicationIntegrationTest {
@Autowired
private TestRestTemplate restTemplate;
@Test
void healthCheck() {
ResponseEntity<String> response = restTemplate.getForEntity("/actuator/health", String.class);
assertEquals(HttpStatus.OK, response.getStatusCode());
}
}
This loads the full Spring context, ideal for validating configurations and third-party integrations.
Mocking with Mockito and @MockBean
Spring Boot integrates seamlessly with Mockito. Use @MockBean
to replace a real bean in the test context.
@MockBean
private NotificationService notificationService;
Mockito will inject the mock automatically into the application context, allowing you to isolate your test subject.
Also use @Captor
for argument capture:
@Captor
ArgumentCaptor<String> emailCaptor;
Testcontainers for Realistic Database Testing
Use Testcontainers to spin up real databases like PostgreSQL, MySQL, or Redis in Docker for your tests.
@Testcontainers
@SpringBootTest
class PostgresIntegrationTest {
@Container
static PostgreSQLContainer<?> postgres = new PostgreSQLContainer<>("postgres:15")
.withDatabaseName("testdb")
.withUsername("user")
.withPassword("pass");
@DynamicPropertySource
static void configProperties(DynamicPropertyRegistry registry) {
registry.add("spring.datasource.url", postgres::getJdbcUrl);
registry.add("spring.datasource.username", postgres::getUsername);
registry.add("spring.datasource.password", postgres::getPassword);
}
@Autowired
private UserRepository repository;
@Test
void shouldSaveAndRetrieveUser() {
User saved = repository.save(new User("Bob", "bob@example.com"));
assertTrue(repository.findById(saved.getId()).isPresent());
}
}
This ensures your application behaves as expected in production-like environments.
Asserting JSON and Response Bodies
Use MockMvc
+ jsonPath
or AssertJ for expressive assertions:
.andExpect(jsonPath("$.email").value("alice@example.com"))
Or parse response bodies into POJOs:
UserDTO user = objectMapper.readValue(response.getContentAsString(), UserDTO.class);
assertEquals("Alice", user.getName());
Testing Security with @WithMockUser
Spring Security tests are simplified using @WithMockUser
:
@Test
@WithMockUser(roles = "ADMIN")
void adminAccessTest() throws Exception {
mockMvc.perform(get("/admin/dashboard"))
.andExpect(status().isOk());
}
Use @WithSecurityContext
to create custom user roles or tokens.
Best Practices for Spring Boot Testing
- Write fast unit tests with mocks and stubs
- Use slices to test specific layers without full context
- Apply Testcontainers for real DB testing over H2
- Leverage
@SpringBootTest
sparingly — it’s heavier - Integrate tests into your CI/CD pipeline
- Ensure tests are isolated, repeatable, and clean up after execution
Conclusion
Advanced testing in Spring Boot helps you validate behavior, catch bugs early, and build resilient applications. With layered testing, testcontainers, mocking, and security integration, you can ensure your application runs reliably in every environment — from development to production.
Invest in your test architecture today, and your codebase will reward you with long-term stability and confidence.