LearnAI ToolsCareerPractice BuildsPlayContact
Lesson 1422 min read

Testing Dependencies

Cover spring-boot-starter-test, spring-security-test for secured endpoints, and Testcontainers for real database integration tests in Docker.

Introduction

Testing a Spring Boot application well requires more than a bare JUnit dependency: you need to load the application context, mock security, and often talk to a real database to catch bugs that an in-memory fake would miss. This lesson covers the three dependencies that cover that full range, from a single unit test to a full integration test running against a real containerized database.

What You Will Learn
  • What spring-boot-starter-test bundles together and why.
  • How @SpringBootTest loads a real application context for integration tests.
  • How spring-security-test's @WithMockUser tests secured endpoints without a real login.
  • How Testcontainers gives you a real, disposable database for integration tests.

spring-boot-starter-test

This single starter bundles everything most projects need for testing: JUnit 5 for writing and running tests, Mockito for mocking dependencies, AssertJ for fluent, readable assertions, and Spring Test for loading application contexts in integration tests.

<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
@SpringBootTest
class OrderServiceIntegrationTest {
@Autowired
private OrderService orderService;
@MockBean
private PaymentGateway paymentGateway;
@Test
void placesOrderWhenPaymentSucceeds() {
when(paymentGateway.charge(any(), anyDouble())).thenReturn(true);
Order order = orderService.placeOrder("cust-1", 49.99);
assertThat(order.getStatus()).isEqualTo(OrderStatus.CONFIRMED);
verify(paymentGateway).charge("cust-1", 49.99);
}
}
What @SpringBootTest Does

Click Run to see what this code prints.

Testing Secured Endpoints

When a controller is protected by Spring Security, a plain MockMvc call gets rejected before it ever reaches your code. spring-security-test's @WithMockUser simulates an authenticated user for the duration of a single test, without needing a real login flow or a token.

<dependency>
<groupId>org.springframework.security</groupId>
<artifactId>spring-security-test</artifactId>
<scope>test</scope>
</dependency>
@SpringBootTest
@AutoConfigureMockMvc
class OrderControllerSecurityTest {
@Autowired
private MockMvc mockMvc;
@Test
void anonymousRequestIsRejected() throws Exception {
mockMvc.perform(get("/api/orders"))
.andExpect(status().isUnauthorized());
}
@Test
@WithMockUser(username = "alice", roles = "CUSTOMER")
void authenticatedRequestSucceeds() throws Exception {
mockMvc.perform(get("/api/orders"))
.andExpect(status().isOk());
}
}

Testcontainers: Real Databases in Docker

An in-memory H2 database is fast but does not behave exactly like PostgreSQL or MySQL — different SQL dialects, different constraint behavior, different edge cases. Testcontainers spins up the real database in a Docker container just for the test run, then tears it down automatically, so your integration tests run against the same engine production uses.

<dependency>
<groupId>org.testcontainers</groupId>
<artifactId>junit-jupiter</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.testcontainers</groupId>
<artifactId>postgresql</artifactId>
<scope>test</scope>
</dependency>
@Testcontainers
@SpringBootTest
class OrderRepositoryIntegrationTest {
@Container
static PostgreSQLContainer<?> postgres =
new PostgreSQLContainer<>("postgres:16-alpine");
@DynamicPropertySource
static void configureDataSource(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 OrderRepository orderRepository;
@Test
void savesAndReadsBackAnOrder() {
Order saved = orderRepository.save(new Order("cust-1", 49.99));
Optional<Order> found = orderRepository.findById(saved.getId());
assertThat(found).isPresent();
assertThat(found.get().getTotal()).isEqualTo(49.99);
}
}
What Happens on Test Run

Click Run to see what this code prints.

Prerequisite

Testcontainers requires a working Docker (or Docker-compatible) runtime on the machine running the tests — this includes CI. Without it, tests annotated with @Testcontainers will fail to start the container.

Choosing the Right Layer

DependencyScopeWhen to Use
spring-boot-starter-testUnit + integrationBaseline for almost every test in the project
spring-security-testSecurity sliceAny test that hits an endpoint protected by Spring Security
testcontainers (junit-jupiter + postgresql)Full integrationRepository or query behavior that must match production database behavior exactly

Common Mistakes

Avoid These Mistakes
  • Using @SpringBootTest for every single test, which loads the full context and makes the suite slow — prefer @WebMvcTest or @DataJpaTest slices where possible.
  • Testing security rules against H2 instead of confirming they also hold with real authentication via @WithMockUser.
  • Relying only on an in-memory database and discovering dialect-specific bugs for the first time in production.
  • Forgetting Docker must be running for Testcontainers-based tests to pass, especially in CI pipelines.

Frequently Asked Questions

No — there are modules for MySQL, MongoDB, Redis, Kafka, Elasticsearch, and many other services, all following the same container-lifecycle pattern.

No, it fabricates an Authentication object directly in the Spring Security context for the duration of the test — no database row is created or required.

Yes, as long as the CI runner has Docker available, which GitHub-hosted runners do by default.

Summary

spring-boot-starter-test covers the everyday unit and integration testing baseline, spring-security-test lets you assert access rules without a real login, and Testcontainers closes the gap between tests and production by running against the real database engine. Next, we turn to observing a running application through Actuator and metrics.

Next Lesson →

Actuator & Monitoring Dependencies