Sau khi phân tích requirement và chia được test condition, cuối cùng cũng đến đoạn mọi người hay gọi là “viết test case”.
Bài trước mình dùng chữ High Level Test Case cho câu mô tả ngắn. Bài này đi vào bản chi tiết hơn, nhiều team gọi là Low Level Test Case. Đây là cách gọi trong dự án chứ không phải hai tầng bắt buộc của ISTQB. Công ty bạn có một template khác thì dùng template của công ty, đừng mang bài này vào họp rồi đòi cả team đổi cột.
Một test case cần trả lời những gì?
Người khác cầm test case lên phải hiểu được:
- Đang kiểm tra điều gì?
- Trước khi chạy cần có trạng thái và dữ liệu gì?
- Phải làm những bước nào?
- Sau mỗi hành động quan trọng, hệ thống phải cho kết quả gì?
- Test chạy ở build và môi trường nào?
Template phổ biến sẽ có các trường sau:
| Trường | Dùng để làm gì? |
|---|---|
| Test Case ID | Định danh để trace, báo cáo và trao đổi |
| Title | Một câu mô tả scenario và kết quả chính |
| Preconditions | Trạng thái phải đúng trước khi bắt đầu |
| Test Data | Account, input, file hoặc dữ liệu cần dùng |
| Steps | Các hành động theo thứ tự |
| Expected Result | Hành vi mong đợi có thể quan sát được |
| Priority | Giúp chọn thứ tự chạy khi không đủ thời gian |
| Status | Trạng thái quản lý test case như Draft, Approved, Obsolete |
Execution status Pass, Fail, Blocked thường thuộc một test run, không phải trạng thái vòng đời của bản thân test case. Tool nào gom hai thứ vào một chỗ thì theo tool, nhưng trong đầu nên tách chúng ra.
Ví dụ: đăng nhập thành công
Giả sử requirement đã làm rõ:
- Account
admin@example.comđang active. - Password đúng là
Correct#123. - Đăng nhập thành công chuyển người dùng tới
/dashboard. - Session được tạo và tên người dùng hiển thị trên header.
Test case có thể viết như này:
| Trường | Nội dung |
|---|---|
| ID | LOGIN-001 |
| Title | Active user logs in successfully with valid credentials |
| Preconditions | User đang ở trang Login; account đang active; chưa có session |
| Test Data | admin@example.com / Correct#123 |
| Priority | P0 |
Các bước:
| # | Action | Expected Result |
|---|---|---|
| 1 | Nhập email admin@example.com | Email hiển thị đúng trong field |
| 2 | Nhập password Correct#123 | Password được che theo thiết kế |
| 3 | Bấm Login | Request được xử lý một lần; user chuyển tới /dashboard; header hiển thị đúng tên account |
Không cần tách “click vào field”, “đưa tay lên bàn phím”, “gõ chữ a” thành ba bước. Test case không phải kịch bản điều khiển robot hình người. Viết đủ để một người khác thực hiện nhất quán, nhưng đừng mô tả những thứ ai mở app cũng nhìn ra.
Expected Result phải kiểm tra được
Một expected result như:
Hệ thống hoạt động đúng.
...thì đúng là đọc xong không biết phải nhìn vào đâu.
Viết cụ thể hơn:
User được chuyển tới
/dashboard, một session hợp lệ được tạo và header hiển thị tênAdmin.
Nếu một step có nhiều kết quả quan trọng, có thể gom chúng khi vẫn dễ đọc. Nếu expected dài cả màn hình thì test case có lẽ đang ôm quá nhiều mục tiêu, lúc đó nên xem lại cách chia scenario.
Precondition không phải bước bị giấu đi
Precondition mô tả state cần có, ví dụ:
- Account đã tồn tại và active.
- Giỏ hàng có một sản phẩm còn tồn kho.
- Feature flag
new-checkoutđang bật.
Nếu muốn tạo state đó bằng UI và bản thân flow tạo state cũng cần được kiểm tra, viết một test case hoặc setup riêng. Còn nếu đây chỉ là điều kiện để test login, đừng bắt người chạy lần nào cũng đăng ký account từ đầu chỉ vì “người dùng phải tới được màn hình này”.
Link sang một test case khác làm precondition có thể tiện, nhưng dễ tạo chuỗi dependency: TC-03 cần TC-02, TC-02 cần TC-01, TC-01 vừa fail là cả bộ nằm im. Ưu tiên setup độc lập và dữ liệu có thể reset khi làm được.
Test Data để riêng hay nhét vào title?
Title nên mô tả scenario:
Active user logs in successfully with valid credentials.
Test data giữ giá trị cụ thể:
email = admin@example.com
password = Correct#123
Nếu giá trị chính là boundary hoặc thứ làm case này khác case kia, đưa nó vào title cũng hợp lý:
Password with exactly 12 characters is accepted.
Không có luật mọi title phải chứa account thật. Nhét data dài ngoằng vào tên sẽ làm report nhìn như một dòng log bị vỡ.
Priority lấy ở đâu?
P0, P1 hay High, Medium, Low đều là quy ước của dự án. Đừng tự gán P0 cho mọi test login rồi tuyên bố “login quan trọng nhất”.
Priority nên nhìn vào:
- Business criticality của flow.
- Rủi ro và lịch sử defect.
- Tần suất người dùng đi qua.
- Test này có chặn nhiều test khác không.
- Chi phí và thời gian thực thi.
Mục tiêu là khi chỉ còn 30 phút, team biết nên chạy gì trước. Nếu case nào cũng P0 thì cột Priority chỉ đang trang trí spreadsheet.
Review test case
Trước khi chạy, tự đọc lại bằng vài câu hỏi:
- Case này trace về requirement hoặc risk nào?
- Preconditions và data có tạo được không?
- Expected result có quan sát được không?
- Case có đang lặp một case khác không?
- Người không viết nó có chạy lại được không?
- Nếu case fail, thông tin có đủ để điều tra không?
Peer review giúp bắt assumption và khoảng coverage bị thiếu. Nó không cần biến thành buổi bắt lỗi dấu chấm phẩy; ưu tiên xem test có đúng và hữu ích trước.

Spreadsheet vẫn dùng được cho bộ nhỏ. Khi số case, người chạy và test run tăng lên, test management tool sẽ đỡ cực hơn.
Chốt lại
Test case tốt không phải test case dài. Nó là test case đủ rõ để người khác chạy, đủ cụ thể để biết pass hay fail và đủ traceability để hiểu tại sao nó tồn tại.
Bài sau đi vào Bug Logging. Test case fail mới chỉ là tín hiệu; biến tín hiệu đó thành một bug report dev reproduce được lại là câu chuyện khác.