Step 6
Step 6 — Testing strategy
25 min
Step 6 — Testing strategy
Tests aren't for later — they're for now. Write them as you code and regression goes nearly to zero.
Three layers
| Layer | Tools | What |
|---|---|---|
| Unit | JUnit 5 + Mockito | one method of one class |
| Integration | @SpringBootTest + Testcontainers |
several beans + real DB |
| E2E | RestAssured / Playwright | HTTP request → response |
For new code, write unit + integration. E2E covers the critical paths.
Integration tests with Testcontainers
@SpringBootTest
@Testcontainers
class PostControllerTest {
@Container
static PostgreSQLContainer<?> postgres = new PostgreSQLContainer<>("postgres:17");
@DynamicPropertySource
static void config(DynamicPropertyRegistry registry) {
registry.add("spring.datasource.url", postgres::getJdbcUrl);
registry.add("spring.datasource.username", postgres::getUsername);
registry.add("spring.datasource.password", postgres::getPassword);
}
@Autowired MockMvc mockMvc;
@Test
void create_post_returns_201() throws Exception {
mockMvc.perform(post("/api/posts")
.contentType("application/json")
.content("""
{"title":"Hi","body":"Hello"}
"""))
.andExpect(status().isCreated())
.andExpect(jsonPath("$.id").exists());
}
}
Docker auto-starts a real PostgreSQL. Better than mocks at catching real bugs.
Unit tests with Mockito
@ExtendWith(MockitoExtension.class)
class PostServiceTest {
@Mock PostRepository repository;
@InjectMocks PostService service;
@Test
void missing_id_throws_NotFoundException() {
when(repository.findById(999L)).thenReturn(Optional.empty());
assertThatThrownBy(() -> service.findById(999L))
.isInstanceOf(NotFoundException.class);
}
}
Try it
Add an integration test for DELETE /api/posts/{id}: (1) create a post, (2) delete it, (3) expect 204, (4) GET expects 404.
Next
Step 7 deploys this with Docker + Caddy.