Khi phát triển ứng dụng Spring Boot, việc kiểm tra tầng Web (Presentation Layer) là bước quan trọng để đảm bảo các API hoạt động đúng. Spring cung cấp cơ chế virtual request giúp mô phỏng HTTP request mà không cần khởi chạy máy chủ thật.
Cấu hình kiểm thử với WebEnvironment
Với môi trường kiểm thử, ta sử dụng MockMvc để gửi các yêu cầu giả lập đến controller và kiểm tra phản hồi. Dưới đây là cách khai báo tiêu chuẩn:
@SpringBootTest
@AutoConfigureMockMvc
class ProductControllerTests {
@Test
void shouldReturnOkStatus(@Autowired MockMvc mvc) throws Exception {
MockHttpServletRequestBuilder request = MockMvcRequestBuilders.get("/products");
ResultActions response = mvc.perform(request);
response.andExpect(MockMvcResultMatchers.status().isOk());
}
}
Kiểm tra trạng thái HTTP
Đoạn mã sau xác minh endpoint trả về mã trạng thái 200 khi tài nguyên tồn tại, và 404 khi đường dẫn không hợp lệ:
@Test
void shouldReturnNotFoundForInvalidPath(@Autowired MockMvc mvc) throws Exception {
MockHttpServletRequestBuilder request = MockMvcRequestBuilders.get("/products/unknown");
ResultActions response = mvc.perform(request);
ResultMatcher notFound = MockMvcResultMatchers.status().isNotFound();
response.andExpect(notFound);
}
Kiểm tra nội dung phản hồi
Bên cạnh trạng thái, ta có thể kiểm tra chuỗi trả về trong body của response:
@Test
void shouldReturnExpectedText(@Autowired MockMvc mvc) throws Exception {
MockHttpServletRequestBuilder request = MockMvcRequestBuilders.get("/products/hello");
ResultActions response = mvc.perform(request);
ResultMatcher expectedBody = MockMvcResultMatchers.content().string("springboot");
response.andExpect(expectedBody);
}
Kiểm tra phản hồi định dạng JSON
Khi API trả về JSON, ta sử dụng matcher json() để so sánh cấu trúc dữ liệu. Chú rằng phương thức này so sánh chính xác các trường trong đối tượng:
@Test
void shouldReturnCorrectJson(@Autowired MockMvc mvc) throws Exception {
MockHttpServletRequestBuilder request = MockMvcRequestBuilders.get("/products/1");
ResultActions response = mvc.perform(request);
String expectedJson = """
{
"productId": 1,
"title": "Spring Boot in Action",
"category": "Backend",
"description": "Guide to Spring Boot"
}
""";
ResultMatcher jsonMatcher = MockMvcResultMatchers.content().json(expectedJson);
response.andExpect(jsonMatcher);
}
Kiểm tra header của phản hồi
Để đảm bảo Content-Type hoặc các header tùy chỉnh được thiết lập đúng, ta dùng HeaderResultMatchers:
@Test
void shouldReturnJsonContentType(@Autowired MockMvc mvc) throws Exception {
MockHttpServletRequestBuilder request = MockMvcRequestBuilders.get("/products/1");
ResultActions response = mvc.perform(request);
ResultMatcher contentTypeHeader = MockMvcResultMatchers.header()
.string("Content-Type", "application/json");
response.andExpect(contentTypeHeader);
}
Kết hợp nhiều điều kiện trong một test
Trong thực tế, một test case thường kết hợp nhiều loại kiểm tra: trạng thái, header, và nội dung JSON. Đoạn mã sau minh họa cách ghép nối chúng:
@Test
void shouldReturnValidProductDetails(@Autowired MockMvc mvc) throws Exception {
MockHttpServletRequestBuilder request = MockMvcRequestBuilders.get("/products/1");
ResultActions response = mvc.perform(request);
ResultMatcher statusMatcher = MockMvcResultMatchers.status().isOk();
ResultMatcher contentTypeMatcher = MockMvcResultMatchers.header()
.string("Content-Type", "application/json");
ResultMatcher bodyMatcher = MockMvcResultMatchers.content().json(
"""
{
"productId": 1,
"title": "Spring Boot in Action",
"category": "Backend",
"description": "Guide to Spring Boot"
}
"""
);
response
.andExpect(statusMatcher)
.andExpect(contentTypeMatcher)
.andExpect(bodyMatcher);
}
Đảm bảo dữ liệu kiểm thử được hoàn tác
Khi test tầng Persistence, các thao tác INSERT/UPDATE/DELETE có thể làm thay đổi cơ sở dữ liệu. ể tự động rollback sau mỗi test, hãy thêm annotation @Transactional từ gói org.springframework.transaction.annotation.Transactional:
@SpringBootTest
@Transactional
class ProductRepositoryTests {
@Autowired
private ProductRepository repository;
@Test
void shouldInsertNewProduct() {
Product entity = new Product();
entity.setTitle("Test Product");
repository.save(entity);
// Sau khi test kết thúc, thay đổi sẽ tự động rollback
}
}
Nạp dữ liệu cấu hình riêng cho test
Đôi khi ta cần một tập giá trị riêng cho các test case, chẳng hạn thông tin sách hoặc sản phẩm mẫu. Có thể dùng @ConfigurationProperties để ánh xạ từ file cấu hình:
package com.example.demo.config;
import lombok.Data;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.stereotype.Component;
@Data
@Component
@ConfigurationProperties(prefix = "testcase.product")
public class TestProductConfig {
private int productId;
private int stock;
private String category;
private String title;
private String uuid;
private long createdAt;
}
Tiếp theo trong file application-test.yml hoặc application-test.properties, định nghĩa các giá trị:
testcase:
product:
product-id: 100
stock: 50
category: "Electronics"
title: "Sample Product"
uuid: "a1b2c3d4-e5f6-7890-abcd-ef1234567890"
created-at: 1700000000000
Việc tách biệt dữ liệu test vào configuration properties giúp dễ bảo trì và tái sử dụng giữa nhiều test case.