System-wide Testing
Chương này thay đổi cách bạn nghĩ về testing. Thay vì test từng hàm nhỏ (unit test truyền thống), hãy test hành vi của cả hệ thống.
1. Vấn đề của unit test truyền thống
Phần tiêu đề “1. Vấn đề của unit test truyền thống”Cách phổ biến: mock mọi dependency, test từng class riêng lẻ.
// Unit test truyền thống — mock TutorAPIfunc testCourseAPI() { let mockTutorAPI = MockTutorAPI() let courseAPI = CourseAPI(tutorAPI: mockTutorAPI) // Test CourseAPI với mock}Vấn đề:
- Test quá gắn chặt với implementation thay vì behavior.
- Refactor một class → hàng loạt test gãy, dù behavior không đổi.
- Phải maintain cả code thật lẫn hàng đống mock object.
2. System-wide Testing: Mock ở tầng thấp nhất
Phần tiêu đề “2. System-wide Testing: Mock ở tầng thấp nhất”Thay vì mock từng dependency:
- Khởi tạo toàn bộ hệ thống thật (CourseAPI → TutorAPI → API → Network).
- Chỉ mock ở biên ngoài cùng — stub network response.
// System-wide test — chỉ mock network responsefunc testLoadCourse() async { let stubbedNetwork = StubbedNetwork(response: fakeCourseJSON) let api = API(network: stubbedNetwork) let tutorAPI = TutorAPI(api: api) let courseAPI = CourseAPI(tutorAPI: tutorAPI)
let course = try await courseAPI.loadCourse()
XCTAssertEqual(course.tutor.name, "Caleb Davis")}3. Tại sao lại tốt hơn?
Phần tiêu đề “3. Tại sao lại tốt hơn?”Refactor thoải mái
Phần tiêu đề “Refactor thoải mái”Vì test chỉ biết về input (network response) và output (kết quả), bạn tự do:
- Đổi tên class
- Tách/gộp class
- Thay đổi data flow bên trong
… mà test vẫn pass, miễn behavior không thay đổi.
Gần với thực tế
Phần tiêu đề “Gần với thực tế”Test chạy gần giống app thật. Bạn phát hiện được lỗi integration — thứ mà unit test truyền thống thường bỏ sót.
Ít mock hơn
Phần tiêu đề “Ít mock hơn”Chỉ cần mock network layer (hoặc database layer). Không cần tạo MockTutorAPI, MockCourseService, MockStore… cho từng class.
4. Nguyên tắc chọn điểm mock
Phần tiêu đề “4. Nguyên tắc chọn điểm mock”Mock ở boundary thấp nhất có thể:
- ✅ Stub network response (HTTP level)
- ✅ Stub database query
- ❌ Mock từng class trung gian
Ngoại lệ: Nếu một dependency thực sự chậm hoặc không ổn định (payment gateway, push notification), hãy mock nó. Nhưng đây là ngoại lệ, không phải mặc định.
5. Trade-off
Phần tiêu đề “5. Trade-off”| Unit test truyền thống | System-wide test | |
|---|---|---|
| Tốc độ | Nhanh hơn | Chậm hơn đôi chút |
| Refactor safety | Thấp — test dễ gãy | Cao — test theo behavior |
| Mock maintenance | Rất nhiều mock | Ít mock |
| Phát hiện lỗi integration | Không | Có |
Kết luận: Đừng test implementation — hãy test behavior. Mock ở tầng thấp nhất có thể, để test giống với cách app chạy thật. Khi refactor, bạn sẽ biết ơn điều này.