Mở đầu — vì sao bài này quan trọng
Hãy tưởng tượng bạn vừa được nhận vào một team QA. Ngày đầu đi làm, sếp giao cho bạn một màn hình đăng nhập và nói: "Em test cái này giúp anh". Nếu bạn chỉ ngồi gõ đúng username, đúng password rồi bấm Login và reo lên "chạy được rồi anh ơi", thì bạn chưa thực sự làm kiểm thử — bạn mới chỉ "dùng thử" phần mềm.
Sự khác biệt giữa một người dùng và một tester nằm ở hai kỹ năng nền tảng mà bài học này sẽ trang bị cho bạn: thiết kế test case (Test Case Design) và báo cáo lỗi (Bug Reporting). Đây chính là hai "sản phẩm" mà một tester tạo ra hằng ngày. Test case là cách bạn biến một yêu cầu mơ hồ thành các bước kiểm tra rõ ràng, có thể lặp lại và đo lường được. Bug report là cách bạn biến một phát hiện "sao nó lạ vậy" thành một tài liệu mà lập trình viên đọc xong là sửa được ngay, không cần hỏi lại.
Trong khi Bài 1 giúp bạn hiểu bức tranh tổng quan về kiểm thử, thì Bài 2 này đưa bạn vào công việc thực chiến hằng ngày. Một tester giỏi test case và giỏi viết bug là người khiến cả team tin tưởng: dev không phải đoán ý, PM nhìn vào là biết chất lượng đang ở đâu, và bản thân bạn không bao giờ "quên test cái gì đó". Đây là nền móng bạn sẽ dùng đến trong suốt sự nghiệp QA, dù sau này bạn có đi theo hướng automation hay quản lý.
Khái niệm cốt lõi
Test case là gì?
Test case là một tập hợp các điều kiện, dữ liệu đầu vào và các bước thao tác, kèm theo kết quả mong đợi, nhằm kiểm tra xem một chức năng cụ thể của phần mềm có hoạt động đúng như yêu cầu hay không. Nói ngắn gọn: test case trả lời câu hỏi "tôi sẽ kiểm tra cái gì, bằng cách nào, và kết quả đúng phải trông ra sao".
Một test case tốt phải có ba đặc tính. Thứ nhất, rõ ràng (clear): một người mới vào team đọc cũng thực thi được mà không cần hỏi. Thứ hai, có thể lặp lại (repeatable): chạy hôm nay hay chạy tháng sau đều cho cùng cách làm. Thứ ba, có kết quả mong đợi cụ thể (verifiable): phải nói rõ "đúng" là như thế nào, chứ không được ghi chung chung kiểu "hệ thống hoạt động bình thường".
Cấu trúc một test case chuẩn
Đây là bộ khung mà hầu hết các công ty tại Việt Nam và khu vực đều dùng. Bạn nên thuộc lòng nó:
| Trường (Field) | Ý nghĩa | Ví dụ |
|---|---|---|
| ID | Mã định danh duy nhất, dễ tra cứu | TC-LOGIN-001 |
| Title | Tiêu đề ngắn mô tả mục tiêu | Đăng nhập thành công với tài khoản hợp lệ |
| Precondition | Điều kiện tiên quyết phải có trước khi test | Đã có tài khoản user01@shop.vn đang active |
| Test Data | Dữ liệu đầu vào cụ thể | Email: user01@shop.vn / Password: Abc@12345 |
| Steps | Các bước thao tác tuần tự | 1. Mở trang login 2. Nhập email 3. Nhập password 4. Bấm "Đăng nhập" |
| Expected Result | Kết quả mong đợi | Chuyển sang trang chủ, hiển thị tên user ở góc phải |
| Actual Result | Kết quả thực tế (điền khi chạy) | (Điền khi thực thi) |
| Status | Trạng thái | Pass / Fail / Blocked / Not Run |
| Priority | Mức độ ưu tiên chạy | High |
Positive vs Negative test case
Người mới thường chỉ viết positive test case — kiểm tra khi mọi thứ đúng luồng (nhập đúng, bấm đúng, ra kết quả đúng). Nhưng phần lớn lỗi nghiêm trọng lại nằm ở negative test case — khi người dùng nhập sai, bỏ trống, nhập ký tự lạ, hoặc cố tình phá. Với màn hình login, positive là "đăng nhập đúng thì vào được"; negative là "sai password 5 lần thì có bị khóa không", "để trống email thì báo lỗi gì", "nhập email chứa dấu tiếng Việt hay khoảng trắng thì sao".
Bug report là gì?
Khi kết quả thực tế khác kết quả mong đợi, bạn đã tìm thấy một bug (lỗi/defect). Nhưng tìm ra lỗi mới là một nửa công việc. Nửa còn lại — quan trọng không kém — là viết bug report sao cho dev tái hiện và sửa được. Một bug report chuẩn gồm các thành phần: Title (tiêu đề súc tích), Steps to Reproduce (các bước tái hiện), Expected Result, Actual Result, Environment (môi trường: trình duyệt, hệ điều hành, phiên bản), Attachment (ảnh chụp màn hình, video, log) và hai trường phân loại là Severity và Priority.
Ở bài này chúng ta chỉ dừng ở mức phân biệt nhanh: Severity là mức độ nghiêm trọng của lỗi về mặt kỹ thuật (lỗi làm sập hệ thống hay chỉ sai chính tả), còn Priority là mức độ cần sửa gấp về mặt kinh doanh. Hai khái niệm này sẽ được đào sâu ở các bài sau, nhưng ngay từ giờ bạn cần nhớ chúng không phải là một.
Tình huống thực tế
Ví dụ 1 — Test case thiếu Precondition ở một sàn TMĐT
Tại một công ty thương mại điện tử tầm trung ở TP.HCM (giả định tên ShopViet, khoảng 40 người), một bạn tester mới viết test case cho chức năng "Áp mã giảm giá". Bạn viết đúng các bước: vào giỏ hàng, nhập mã SALE50, bấm áp dụng, kỳ vọng giảm 50.000đ. Khi bạn chạy thì Pass. Nhưng khi bàn giao cho đồng nghiệp chạy lại trước ngày release, đồng nghiệp báo Fail vì mã báo "không hợp lệ".
Nguyên nhân: mã SALE50 chỉ áp dụng cho đơn từ 300.000đ trở lên và chỉ dùng được 1 lần/tài khoản. Bạn tester đầu tiên vô tình có sẵn giỏ hàng 350.000đ và tài khoản chưa dùng mã, nên qua. Đồng nghiệp thì giỏ chỉ 150.000đ. Test case thiếu hẳn phần Precondition "giỏ hàng >= 300.000đ, tài khoản chưa từng dùng mã SALE50".
Bài học: Một test case không có Precondition rõ ràng thì kết quả Pass/Fail trở nên phụ thuộc vào may rủi. Precondition không phải phần trang trí — nó là điều kiện quyết định test case có tái hiện được hay không.
Ví dụ 2 — Bug report "chung chung" khiến dev từ chối sửa
Cũng tại một startup fintech ở Hà Nội, tester log một bug với tiêu đề: "App bị lỗi khi thanh toán". Phần mô tả chỉ vỏn vẹn: "Em thanh toán mà nó không được, anh xem giúp". Dev nhận bug, thử thanh toán vài lần thì thành công, bèn đóng bug với trạng thái "Cannot Reproduce" (không tái hiện được). Bug bị bỏ qua.
Ba ngày sau, khách hàng thật gặp đúng lỗi đó và phàn nàn lên fanpage: khi số tiền thanh toán có phần lẻ dạng 1.000.500đ và mạng chậm, giao dịch bị treo màn hình loading vĩnh viễn. Hóa ra lỗi chỉ xảy ra trên mạng 3G yếu + số tiền có phần nghìn lẻ, trên thiết bị Samsung Galaxy A12, Android 11. Nếu bug report ban đầu ghi rõ steps, số tiền cụ thể, thiết bị và điều kiện mạng, dev đã tái hiện và vá được ngay từ đầu, tránh được khủng hoảng truyền thông nhỏ.
Bài học: Bug report thiếu Steps to Reproduce và Environment gần như vô dụng. Một bug không tái hiện được sẽ bị đóng, và lỗi sẽ quay lại đúng lúc bạn không mong muốn nhất — trên tay khách hàng thật.
Ví dụ 3 — Thiếu negative test case cho ô tìm kiếm
Một team làm ứng dụng đặt đồ ăn (giả định FoodNow) chỉ viết positive test case cho ô tìm kiếm nhà hàng: gõ "phở" ra danh sách quán phở, gõ "trà sữa" ra danh sách trà sữa. Tất cả đều Pass, sản phẩm lên production. Vài ngày sau, một người dùng vô tình dán cả đoạn văn dài kèm ký tự đặc biệt '; DROP TABLE vào ô tìm kiếm, và ứng dụng văng lỗi trắng màn hình, để lộ thông báo lỗi hệ thống.
Nếu team đã thiết kế negative test case — nhập chuỗi rỗng, nhập ký tự đặc biệt, nhập chuỗi cực dài, nhập ký tự tiếng Việt có dấu — thì lỗ hổng này đã được phát hiện từ giai đoạn test.
Bài học: Phần lớn lỗi nghiêm trọng ẩn ở luồng bất thường. Một bộ test case chỉ toàn positive là một bộ test case "ngây thơ".
Hướng dẫn từng bước
Đây là quy trình bạn có thể áp dụng ngay cho bất kỳ chức năng nào được giao.
Bước 1 — Đọc và hiểu yêu cầu. Đọc kỹ user story hoặc mô tả chức năng. Gạch chân các quy tắc nghiệp vụ (business rule): điều kiện, giới hạn, thông báo. Nếu có chỗ mơ hồ, hỏi lại BA/PM ngay — đừng đoán.
Bước 2 — Liệt kê các kịch bản (scenario). Trước khi viết chi tiết, hãy liệt kê nhanh các tình huống cần kiểm tra dưới dạng gạch đầu dòng: luồng đúng, luồng sai, trường hợp biên, trường hợp ngoại lệ. Ví dụ với login: đăng nhập đúng, sai password, sai email, bỏ trống, tài khoản bị khóa, nhập nhiều lần sai.
Bước 3 — Viết test case chi tiết theo template. Với mỗi scenario, điền đầy đủ các trường: ID, Title, Precondition, Test Data, Steps, Expected Result. Nhớ dùng ID có quy tắc (ví dụ TC-LOGIN-001) để dễ tra cứu và trace về sau.
Bước 4 — Rà soát chéo (review). Đọc lại và tự hỏi: người khác đọc có làm theo được không? Expected Result đã cụ thể chưa? Đã có cả positive lẫn negative chưa? Nếu được, nhờ một đồng nghiệp đọc thử.
Bước 5 — Thực thi và ghi nhận. Chạy từng test case, điền Actual Result và Status. Khi gặp Fail, chuyển sang quy trình viết bug.
Bước 6 — Viết bug report khi phát hiện lỗi. Đặt Title theo công thức "[Màn hình] Hiện tượng khi điều kiện" — ví dụ "[Thanh toán] Màn hình treo loading khi số tiền có phần nghìn lẻ trên 3G". Ghi Steps to Reproduce đánh số rõ ràng, kèm Expected và Actual, đính kèm ảnh/video, ghi rõ Environment. Gán Severity và Priority sơ bộ.
Lỗi thường gặp & mẹo
- Expected Result mơ hồ. Đừng viết "hệ thống hoạt động đúng". Hãy viết cụ thể: "Chuyển sang trang chủ, hiển thị 'Xin chào, Khang' ở góc phải trên".
- Gộp nhiều mục tiêu vào một test case. Một test case chỉ nên kiểm một mục tiêu. Nếu một case vừa test login đúng vừa test đổi mật khẩu, khi Fail bạn sẽ không biết lỗi nằm ở đâu.
- Bỏ quên Precondition và Test Data. Đây là nguyên nhân số một khiến test case không tái hiện được, như ví dụ ShopViet ở trên.
- Chỉ viết positive test case. Luôn tự hỏi "điều gì xảy ra nếu người dùng làm sai?".
- Bug Title kiểu than vãn. "App lỗi quá!" là vô ích. Title phải nói được lỗi gì, ở đâu, khi nào.
- Không đính kèm bằng chứng. Một ảnh chụp màn hình hoặc đoạn video 10 giây tiết kiệm cho dev hàng giờ điều tra.
- Mẹo tiếng Việt: Luôn có ít nhất một test case dùng dữ liệu tiếng Việt có dấu (ví dụ họ tên "Nguyễn Thị Hồng Nhung") vì lỗi encoding rất hay xảy ra và dễ bị bỏ sót.
- Mẹo vàng: Trước khi log bug, hãy tự tái hiện lại lỗi ít nhất một lần theo đúng steps bạn viết. Nếu chính bạn còn không tái hiện được, dev cũng sẽ không.
Bài tập thực hành
- Thiết kế test case cho màn hình đăng ký tài khoản của một app bất kỳ. Yêu cầu tối thiểu: 3 positive và 5 negative test case, đầy đủ các trường ID, Title, Precondition, Test Data, Steps, Expected Result. Phải có ít nhất một case dùng tên tiếng Việt có dấu và một case để trống trường bắt buộc.
- Viết lại một bug report tệ thành bug report tốt. Cho bug gốc: "Nút mua hàng không bấm được". Hãy bổ sung Title chuẩn, Steps to Reproduce, Expected/Actual Result, Environment và đề xuất Severity/Priority sơ bộ (tự giả định bối cảnh hợp lý).
- Rà soát chéo. Đổi test case ở bài 1 với một người bạn cùng học, thử thực thi theo đúng những gì họ viết mà không hỏi thêm. Ghi lại những chỗ bạn bị "kẹt" vì thiếu thông tin — đó chính là những điểm cần cải thiện.
Tóm tắt
Test case và bug report là hai sản phẩm cốt lõi mà một tester tạo ra mỗi ngày. Một test case tốt phải rõ ràng, lặp lại được và có kết quả mong đợi cụ thể, với cấu trúc chuẩn gồm ID, Title, Precondition, Test Data, Steps, Expected Result và Status. Đừng bao giờ bỏ quên Precondition, Test Data, và luôn cân bằng giữa positive và negative test case — vì phần lớn lỗi nghiêm trọng ẩn ở luồng bất thường.
Một bug report tốt phải giúp dev tái hiện và sửa được ngay: Title súc tích theo công thức "[Màn hình] Hiện tượng khi điều kiện", Steps to Reproduce đánh số rõ, Expected vs Actual, Environment đầy đủ và bằng chứng đính kèm. Ba ví dụ thực tế — ShopViet, startup fintech và FoodNow — cho thấy cái giá của việc làm ẩu: test không tái hiện được, bug bị đóng nhầm, và lỗ hổng lọt ra tay khách hàng thật.
Hãy luyện hai kỹ năng này thật vững, vì chúng là nền móng cho mọi thứ bạn làm về sau trong nghề QA.