Spring Boot bài 26: Integration test MVC, API, database và Security
- 18-10-2026
- Toanngo92
- 0 Comments
Giáo trình Spring Boot MVC và REST API · Chương 11/12: Kiểm thử Service, MVC và API · Bài 26/28 · Biên soạn tháng 10/2026.
Mục lục
Mục tiêu bài học
Kiểm tra các thành phần cùng hoạt động bằng MockMvc, bao gồm render template, validation, quyền và CRUD API.
Khác với unit test
@SpringBootTest tạo application context thực của mẫu; @AutoConfigureMockMvc cung cấp MockMvc để gửi request qua hạ tầng MVC và filter mà không mở cổng mạng thật. Với Boot 4, annotation MockMvc thuộc package org.springframework.boot.webmvc.test.autoconfigure; mã nguồn mẫu đã dùng đúng import.
Cấu hình trong src/test/resources dùng H2 riêng, tắt email và scheduling, có mật khẩu chỉ dành cho test. Flyway tạo schema rồi Hibernate validate. Điều đó kiểm tra nhiều mắt xích mà mock repository không thể thấy: mapping entity, tên bảng, template, JSON và SecurityFilterChain.
Tệp trong mã nguồn mẫu: src/test/java/com/hocvietcode/academy/WebIntegrationTest.java. Đoạn dưới được trích từ tệp này; xem bản tải về để có đủ import và phần còn lại.
@SpringBootTest @AutoConfigureMockMvc @Transactional
class WebIntegrationTest {
@Autowired MockMvc mvc; @Autowired CourseRepository repository;
@Test void publicListRendersEscapedHtmlAndPagination()throws Exception{
mvc.perform(get("/courses").param("size","1")).andExpect(status().isOk()).andExpect(view().name("courses/list"))
.andExpect(content().string(org.hamcrest.Matchers.containsString("page=1")));
}
@Test void detailRenders()throws Exception{mvc.perform(get("/courses/1")).andExpect(status().isOk());}
@Test void formContainsCsrfToken()throws Exception{
mvc.perform(get("/admin/courses/new").with(user("teacher").roles("TEACHER")))
.andExpect(status().isOk()).andExpect(content().string(org.hamcrest.Matchers.containsString("name=\"_csrf\"")));
}
Kiểm tra cả trường hợp bị từ chối
Teacher tạo thành công chưa đủ. Cần student có CSRF nhưng vẫn bị 403, teacher thiếu CSRF bị 403, form sai không tăng số bản ghi, tài nguyên thiếu trả 404 và form render có input _csrf. Kiểm tra HTML thật giúp bắt lỗi template mà chỉ assert tên view có thể bỏ sót.
Các test .with(user(…)) giả lập authentication để tập trung vào authorization. Nó không kiểm tra mật khẩu thật. Vì vậy bộ mẫu có thêm loginWorksWithConfiguredPassword gửi POST /login với CSRF và password test để kiểm tra luồng xác thực thực tế.
Tệp trong mã nguồn mẫu: src/test/java/com/hocvietcode/academy/WebIntegrationTest.java. Đoạn dưới được trích từ tệp này; xem bản tải về để có đủ import và phần còn lại.
@Test void invalidFormKeepsErrorsAndDoesNotSave()throws Exception{
long count=repository.count();
mvc.perform(post("/admin/courses").with(user("teacher").roles("TEACHER")).with(csrf())
.param("title","").param("price","-1").param("categoryId","1"))
.andExpect(status().isOk()).andExpect(model().attributeHasFieldErrors("form","title","price"));
assertThat(repository.count()).isEqualTo(count);
}
API cần kiểm tra hợp đồng HTTP
ApiIntegrationTest xác nhận envelope danh sách, ProblemDetail 404, validation 400, token không cache, CORS origin hợp lệ và origin lạ. crudRoundTrip tạo, sửa, xóa rồi đọc lại để kiểm tra toàn luồng. Test không chỉ nhìn JSON có một title mà còn xác nhận status và Location.
@Transactional trên test giúp rollback thay đổi DB khi chạy cùng luồng MockMvc này. Nếu chuyển sang webEnvironment=RANDOM_PORT và HTTP thật, server chạy ở luồng/giao dịch khác; rollback test không tự xóa mọi thay đổi của server. Khi đó cần chiến lược dọn dữ liệu hoặc DB dùng một lần.
H2 và MySQL cần hai mức bằng chứng
H2 nhanh và thuận tiện nhưng MODE=MySQL không mô phỏng đầy đủ MySQL. Trước triển khai, chạy migration và smoke test trên MySQL đúng phiên bản hoặc dùng Testcontainers trong pipeline phù hợp. Không chạy test xóa dữ liệu trên database đang phục vụ học viên.
Mail và HTTP ngoài được mock trong test chuyên biệt để tránh gửi thư hoặc phụ thuộc mạng. Upload có test tệp hợp lệ, quá dung lượng, sai loại và đường dẫn không hợp lệ. Một bộ test có giới hạn được mô tả rõ sẽ hữu ích hơn lời khẳng định “đã test tất cả” mà không biết đang dùng database và môi trường nào.
.\mvnw.cmd test
# Báo cáo XML/text: target/surefire-reports/
# Chạy riêng nhóm API khi cần chẩn đoán:
.\mvnw.cmd -Dtest=ApiIntegrationTest test
Lỗi thường gặp và lưu ý
- Không tắt filter trong MockMvc rồi dùng kết quả đó để kết luận phân quyền đã đúng.
- Transactional test rollback không tự bao phủ request chạy ở luồng của web server thật.
Ghi nhớ
Integration test xác nhận đường nối giữa các thành phần, gồm cả nhánh lỗi và quyền, với giới hạn môi trường được nêu rõ.

