0

[Java Backend Zero to Hello] BÀI 7.3: INTEGRATION TEST

Java Backend Zero to Hello

📚 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

Viblo
Hãy đăng ký một tài khoản Viblo để nhận được nhiều bài viết thú vị hơn.
Đăng kí