Dev tự code và verify thì test thế nào cho đúng và đủ?
Rất nhiều bạn mới chuyển từ QA sang Dev, hoặc làm ở team không có QA riêng, thường nghĩ:
“Code chạy được, unit test pass là xong.”
Đó là một cột mốc quan trọng, nhưng chưa phải điểm kết thúc.
Thực tế, rất nhiều bug lọt lên production không phải vì code sai logic, mà vì chưa được kiểm chứng ở góc nhìn của người dùng.
Dev tự verify và QA test khác nhau ở đâu?
Điểm khác biệt không nằm ở kỹ năng, mà nằm ở cách tư duy.
Khi dev tự test code của mình, chúng ta thường nhìn bài toán từ trong ra ngoài (white-box).
- Biết rõ code hoạt động như thế nào.
- Biết những nhánh
if/elsenào vừa sửa. - Biết dữ liệu nào sẽ đi qua từng đoạn logic.
Vì vậy, việc test thường chỉ để xác nhận:
“Mình đã code đúng theo những gì mình nghĩ.”
Ngược lại, QA tiếp cận theo hướng từ ngoài vào trong (black-box).
QA không quan tâm bên trong có bao nhiêu class hay hàm. Điều họ quan tâm là:
- Chức năng có đúng với yêu cầu nghiệp vụ không?
- Người dùng thao tác sai thì chuyện gì xảy ra?
- Input bất thường có làm hệ thống lỗi không?
- Việc sửa này có ảnh hưởng đến chức năng khác không?
Nói ngắn gọn:
Dev kiểm tra xem code có chạy đúng.
QA kiểm tra xem sản phẩm có hoạt động đúng.
Đó là hai góc nhìn bổ sung cho nhau, chứ không phải thay thế nhau.
Nếu không còn QA thì Dev cần làm gì?
Trong nhiều team hiện nay, Dev phải tự chịu trách nhiệm verify trước khi tạo Pull Request.
Điều quan trọng nhất là tạm quên mình là người viết code.
Hãy mở lại ticket và đọc yêu cầu như thể đây là lần đầu tiên bạn nhìn thấy chức năng đó.
Sau đó tự đặt câu hỏi:
1. Happy path
Luồng sử dụng bình thường có hoạt động đúng không?
Ví dụ:
- Người dùng nhập đúng dữ liệu
- Bấm Submit
- Kết quả đúng như Acceptance Criteria
2. Negative case
Nếu người dùng thao tác sai thì sao?
- Thiếu dữ liệu
- Input sai định dạng
- Giá trị
null - Không đủ quyền
- API trả lỗi
Ứng dụng có xử lý đúng và hiển thị thông báo phù hợp không?
3. Edge case
Đây là nơi bug thường xuất hiện.
Ví dụ:
- Giá trị min/max
- Chuỗi rất dài
- Ký tự đặc biệt
- Dữ liệu trùng lặp
- Danh sách rỗng
- Dữ liệu cực lớn
Đừng chỉ test những gì “đẹp”. Hãy test cả những gì người dùng có thể làm, dù nghe có vẻ vô lý.
4. Regression
Đừng chỉ nhìn vào màn hình đang sửa.
Hãy tự hỏi:
Đoạn code này còn được dùng ở đâu nữa?
Một thay đổi nhỏ trong API, model hay component dùng chung có thể làm hỏng nhiều chức năng khác mà bạn không để ý.
5. Hành vi thực tế của người dùng
Người dùng không thao tác “đẹp” như trong test case.
Họ có thể:
- Bấm liên tục nhiều lần
- Refresh giữa lúc submit
- Back giữa quy trình
- Mất mạng
- Mở nhiều tab cùng lúc
Nếu chưa thử các tình huống này, bạn mới chỉ kiểm tra logic, chưa thực sự kiểm tra sản phẩm.
Vì sao unit test pass vẫn chưa đủ?
Đây là hiểu lầm phổ biến nhất.
Unit test chỉ chứng minh một đoạn code hoạt động đúng trong điều kiện mà chính bạn tạo ra.
Trong unit test, hầu hết dependency đều được mock:
- Database
- API
- Service khác
- Cache
- Message Queue
Nghĩa là bạn đang kiểm tra:
“Nếu mọi thứ xung quanh đều hoàn hảo thì hàm của mình có đúng không?”
Nhưng production thì không hoàn hảo.
Bug thực tế thường xuất hiện ở những điểm mà unit test không chạm tới, chẳng hạn:
- Sai mapping request/response
- API trả dữ liệu khác mong đợi
- Timeout
- Race condition
- Transaction lỗi
- Sai cấu hình
- UI hiển thị sai dù backend đúng
Đó đều là những lỗi mà unit test rất khó phát hiện.
Muốn tự tin nói “Task Done” cần kiểm tra đủ 3 tầng
Một task chỉ nên được xem là hoàn thành khi đã được xác nhận ở nhiều mức độ khác nhau.
1. Unit Test
- Logic từng hàm đúng
- Chạy nhanh
- Bao phủ nhiều nhánh xử lý
2. Integration / API Test
- Gọi API thật hoặc môi trường gần giống production
- Kiểm tra request/response
- Xác nhận xử lý lỗi từ service khác
- Verify dữ liệu trong database nếu cần
3. UI Test (Manual hoặc E2E)
- Thao tác như người dùng thật
- Đối chiếu Acceptance Criteria
- Kiểm tra toàn bộ luồng nghiệp vụ
Đây cũng chính là tư tưởng của Test Pyramid:
- Unit Test là nền móng: nhiều, nhanh và chạy thường xuyên.
- Integration Test xác nhận các thành phần làm việc đúng với nhau.
- UI/E2E Test đảm bảo người dùng thực sự sử dụng được chức năng.
Kết luận
Code chạy không đồng nghĩa với sản phẩm đúng.
Unit test pass cũng không đồng nghĩa với bug đã được loại bỏ.
Khi không còn QA, Dev không chỉ là người viết code mà còn phải là người kiểm chứng chất lượng.
Muốn tự tin merge một Pull Request, hãy tự hỏi:
“Nếu mình là QA, mình sẽ tìm cách làm hỏng chức năng này như thế nào?”
Chỉ cần trả lời được câu hỏi đó trước khi tạo PR, chất lượng code và khả năng phát hiện bug của bạn sẽ tăng lên đó.


