[Java Backend Zero to Hello] BÀI 7.3: INTEGRATION TEST
📚 Bài viết thuộc series Java Backend Zero to Hello 📌 Phần: Phase 7: Testing & DevOps Cơ Bản | Bài 73/86
BÀI 7.3: INTEGRATION TEST
Mục tiêu
- Viết Integration Test với Spring Boot
- Sử dụng Testcontainers
- Test REST API với MockMvc
- Test với database thật
1. CÁC LOẠI TEST
| Loại | Phạm vi | Tốc độ |
|---|---|---|
| Unit Test | Method/class | Nhanh |
| Integration Test | Nhiều component | Trung bình |
| E2E Test | Toàn bộ hệ thống | Chậm |
2. @SPRINGBOOTTEST
Load toàn bộ ApplicationContext.
@SpringBootTest
class UserServiceIntegrationTest {
@Autowired
private UserService userService;
@Autowired
private UserRepository userRepository;
@Test
void shouldCreateAndFindUser() {
User user = new User(null, "An", "an@example.com", 25);
User saved = userService.create(user);
User found = userService.findById(saved.getId());
assertThat(found.getName()).isEqualTo("An");
}
}
3. @WEBMVCTEST
Test riêng Controller layer.
@WebMvcTest(UserController.class)
class UserControllerTest {
@Autowired
private MockMvc mockMvc;
@MockBean
private UserService userService;
@Test
void shouldReturnUser() throws Exception {
UserResponse user = new UserResponse(1L, "An", "an@example.com", 25, null);
when(userService.findById(1L)).thenReturn(user);
mockMvc.perform(get("/api/users/1"))
.andExpect(status().isOk())
.andExpect(jsonPath("$.name").value("An"))
.andExpect(jsonPath("$.email").value("an@example.com"));
}
@Test
void shouldReturn404WhenNotFound() throws Exception {
when(userService.findById(999L))
.thenThrow(new NotFoundException("User not found"));
mockMvc.perform(get("/api/users/999"))
.andExpect(status().isNotFound());
}
@Test
void shouldCreateUser() throws Exception {
CreateUserRequest request = new CreateUserRequest("An", "an@example.com", 25);
UserResponse response = new UserResponse(1L, "An", "an@example.com", 25, null);
when(userService.create(any())).thenReturn(response);
mockMvc.perform(post("/api/users")
.contentType(MediaType.APPLICATION_JSON)
.content("""
{
"name": "An",
"email": "an@example.com",
"age": 25
}
"""))
.andExpect(status().isCreated())
.andExpect(jsonPath("$.id").value(1));
}
}
4. @DATAJPASTEST
Test riêng Repository layer.
@DataJpaTest
class UserRepositoryTest {
@Autowired
private UserRepository userRepository;
@Autowired
private TestEntityManager entityManager;
@Test
void shouldFindByEmail() {
User user = new User(null, "An", "an@example.com", 25);
entityManager.persistAndFlush(user);
Optional<User> found = userRepository.findByEmail("an@example.com");
assertThat(found).isPresent();
assertThat(found.get().getName()).isEqualTo("An");
}
}
5. TEST VỚI H2 DATABASE
# application-test.yml
spring:
datasource:
url: jdbc:h2:mem:testdb
driver-class-name: org.h2.Driver
jpa:
hibernate:
ddl-auto: create-drop
properties:
hibernate:
dialect: org.hibernate.dialect.H2Dialect
@SpringBootTest
@ActiveProfiles("test")
class IntegrationTest {
// ...
}
6. TESTCONTAINERS
Dùng Docker để chạy database thật (PostgreSQL, MySQL, ...).
6.1 Dependency
<dependency>
<groupId>org.testcontainers</groupId>
<artifactId>testcontainers</artifactId>
<version>1.19.7</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.testcontainers</groupId>
<artifactId>postgresql</artifactId>
<version>1.19.7</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.testcontainers</groupId>
<artifactId>junit-jupiter</artifactId>
<version>1.19.7</version>
<scope>test</scope>
</dependency>
6.2 Sử dụng
@SpringBootTest
@Testcontainers
class UserRepositoryIntegrationTest {
@Container
@ServiceConnection
static PostgreSQLContainer<?> postgres = new PostgreSQLContainer<>("postgres:16")
.withDatabaseName("testdb")
.withUsername("test")
.withPassword("test");
@Autowired
private UserRepository userRepository;
@Test
void shouldSaveAndFindUser() {
User user = new User(null, "An", "an@example.com", 25);
User saved = userRepository.save(user);
Optional<User> found = userRepository.findById(saved.getId());
assertThat(found).isPresent();
}
}
6.3 Container chung
@Testcontainers
public class BaseIntegrationTest {
@Container
@ServiceConnection
protected static final PostgreSQLContainer<?> POSTGRES =
new PostgreSQLContainer<>("postgres:16");
@Container
@ServiceConnection
protected static final RedisContainer REDIS =
new RedisContainer(DockerImageName.parse("redis:7"));
}
7. TEST REST API VỚI TESTRESTTEMPLATE
@SpringBootTest(webEnvironment = WebEnvironment.RANDOM_PORT)
class UserApiIntegrationTest {
@Autowired
private TestRestTemplate restTemplate;
@Test
void shouldCreateAndGetUser() {
CreateUserRequest request = new CreateUserRequest("An", "an@example.com", 25);
ResponseEntity<UserResponse> createResponse = restTemplate
.postForEntity("/api/users", request, UserResponse.class);
assertThat(createResponse.getStatusCode()).isEqualTo(HttpStatus.CREATED);
Long userId = createResponse.getBody().getId();
ResponseEntity<UserResponse> getResponse = restTemplate
.getForEntity("/api/users/" + userId, UserResponse.class);
assertThat(getResponse.getStatusCode()).isEqualTo(HttpStatus.OK);
assertThat(getResponse.getBody().getName()).isEqualTo("An");
}
}
8. @SQL VÀ @SQLGROUP
@SpringBootTest
@Sql(scripts = "/test-data.sql", executionPhase = Sql.ExecutionPhase.BEFORE_TEST_METHOD)
class UserServiceTest {
@Test
void shouldFindUser() {
// Test với data đã load
}
}
9. TEST PROFILE
@SpringBootTest
@ActiveProfiles("test")
@TestPropertySource(properties = {
"spring.datasource.url=jdbc:h2:mem:test",
"logging.level.com.example=DEBUG"
})
class CustomIntegrationTest {
}
10. BÀI TẬP THỰC HÀNH
Bài 1: Controller Test
Test UserController với MockMvc.
Bài 2: Repository Test
Test UserRepository với @DataJpaTest.
Bài 3: Full Integration Test
Test toàn bộ flow với Testcontainers.
11. TÓM TẮT
| Annotation | Mô tả |
|---|---|
| @SpringBootTest | Load full context |
| @WebMvcTest | Test controller |
| @DataJpaTest | Test repository |
| @MockBean | Mock bean |
| Testcontainers | Docker container |
| @ActiveProfiles | Chọn profile |
| @Sql | Chạy SQL script |
Bài tiếp theo: 7.4 Docker cơ bản
🧭 Điều Hướng Series
⬅️ Bài trước: BÀI 7.2: MOCKITO
📋 Lộ trình tổng quan: Xem Toàn Bộ Series
➡️ Bài tiếp theo: BÀI 7.4: DOCKER CƠ BẢN
All rights reserved