Trang chủ
QA Learning Hub
Chương 01 · Test Cases
Chương 01 Cơ Bản

Viết Test Cases

Nền tảng của mọi QA engineer: cách thiết kế, viết và duy trì test case chuyên nghiệp. Từ cấu trúc 9 thành phần đến các kỹ thuật thiết kế nâng cao.

📋Cấu Trúc Test Case Chuẩn

Một test case chuẩn công nghiệp gồm 9 thành phần. Thiếu bất kỳ thành phần nào sẽ khiến test case khó tái sử dụng, khó debug và khó maintain.

  • Test Case ID Mã định danh duy nhất. Format: [Module]-[Type]-[Number]. Ví dụ: AUTH-POS-001 (Authentication, Positive, case số 1). Đừng bao giờ dùng số thứ tự đơn giản như TC001 vì khó biết nó thuộc module nào.
  • Title / Summary Mô tả ngắn gọn, rõ ràng. Phải trả lời được "test gì" chỉ bằng cách đọc title. Xấu: "Test login". Tốt: "User logs in successfully with valid email and password".
  • Preconditions Điều kiện phải thỏa mãn TRƯỚC khi chạy test. Ví dụ: "User account exists with email test@example.com", "Browser cleared cache", "Feature flag LOGIN_V2 is enabled". Thiếu preconditions → test flaky.
  • Test Data Dữ liệu cụ thể dùng trong test. Không viết "nhập email hợp lệ" — phải là "nhập qa.test+001@example.com". Specificity làm test reproducible.
  • Test Steps Từng bước thực hiện theo thứ tự. Mỗi step = một hành động duy nhất. Xấu: "Mở browser, đăng nhập, vào profile". Tốt: tách thành 3 steps riêng. Dùng động từ: Click, Enter, Select, Navigate, Verify.
  • Expected Result Kết quả mong đợi cho MỖI step quan trọng và kết quả tổng thể. Phải measurable và observable. Xấu: "Login thành công". Tốt: "User được redirect sang /dashboard, hiển thị welcome message với username, session cookie được set với httpOnly flag".
  • Priority P1 (Critical/Blocker), P2 (High), P3 (Medium), P4 (Low). Priority xác định thứ tự thực hiện và ảnh hưởng đến go/no-go decision. P1 failures = stop release.
  • Test Type Positive / Negative / Edge Case / Performance / Security. Một feature cần đủ cả 4 loại mới gọi là coverage tốt.
  • Traceability Link đến requirement/user story. Ví dụ: REQ-AUTH-001, JIRA: AUTH-123. Đây là nền tảng của RTM (Requirements Traceability Matrix).

Ví dụ Test Case Hoàn Chỉnh — Feature: Login

FieldNội dung
TC IDAUTH-POS-001
TitleĐăng nhập thành công với email và password hợp lệ
PriorityP1 — Critical
TypePositive
Req LinkAUTH-REQ-001, JIRA: AUTH-45
Preconditions1. User account tồn tại với email qa.user@test.com / password Test@1234
2. Account không bị locked
3. Browser: Chrome latest, cache cleared
Test DataEmail: qa.user@test.com
Password: Test@1234
Test Steps 1. Navigate đến https://app.example.com/login
2. Enter email vào field "Email address"
3. Enter password vào field "Password"
4. Click button "Sign In"
Expected Result - Sau step 4: HTTP 200, redirect sang /dashboard
- Hiển thị: "Welcome back, QA User!"
- Session cookie được set (httpOnly, Secure flags)
- Navigation bar hiển thị avatar và username
- Login history được ghi lại (verify trong DB nếu có access)
Pass/Fail[ ] Pass   [ ] Fail   [ ] Blocked
NotesNếu redirect về /login lại → check network tab xem API response code
Tip
Viết expected result trước khi viết test steps. Nếu bạn không biết expected result trông như thế nào, bạn chưa hiểu rõ requirement đủ để test.

🧪6 Kỹ Thuật Thiết Kế Test Case

Biết kỹ thuật giúp bạn tạo ra test cases có coverage tốt nhất với số lượng ít nhất — đây là điểm phân biệt QA giỏi và QA bình thường.

1. Equivalence Partitioning (EP)

Chia input domain thành các nhóm (partition) mà các giá trị trong cùng nhóm được xử lý giống nhau. Test 1 đại diện cho cả nhóm.

Ví dụ: Field "Tuổi" chấp nhận 18-65. Có 3 partition:

  • Invalid low: < 18 (ví dụ: 15) → expect error
  • Valid: 18-65 (ví dụ: 30) → expect accept
  • Invalid high: > 65 (ví dụ: 70) → expect error

Chỉ cần 3 test cases thay vì test từng số từ 0-100.

2. Boundary Value Analysis (BVA)

Test tại các giá trị biên vì bugs thường xuất hiện ở đây (off-by-one errors). Với mỗi boundary, test: giá trị tại boundary, 1 dưới, 1 trên.

Ví dụ cùng field Tuổi 18-65:

Test ValueTypeExpected
17Below lower boundaryError: "Tuổi phải từ 18"
18At lower boundaryAccept
19Above lower boundaryAccept
64Below upper boundaryAccept
65At upper boundaryAccept
66Above upper boundaryError: "Tuổi phải đến 65"

3. Decision Table Testing

Dùng khi có nhiều conditions kết hợp với nhau. Liệt kê tất cả combinations của conditions và expected outcomes.

Ví dụ: Login với conditions: email valid (Y/N) × password correct (Y/N) × account active (Y/N):

Email validPassword đúngAccount activeExpected result
YYYLogin thành công
YYNError: "Tài khoản bị khóa"
YNYError: "Sai mật khẩu"
YNNError: "Sai mật khẩu" (account check sau)
N--Error: "Email không tồn tại"

4. State Transition Testing

Dùng cho systems có states rõ ràng. Test các transition hợp lệ và không hợp lệ.

Ví dụ Order states: Pending → Processing → Shipped → DeliveredPending/Processing → Cancelled

  • Test happy path: Pending → Processing → Shipped → Delivered
  • Test cancellation: Pending → Cancelled (valid)
  • Test invalid: Delivered → Pending (không cho phép)
  • Test invalid: Shipped → Cancelled (tuỳ business rule)

5. Error Guessing

Dựa trên kinh nghiệm và trực giác để đoán các nơi hay có bug. Không theo quy tắc cố định — đây là "nghệ thuật" của QA lâu năm.

  • Nhập ký tự đặc biệt: <script>alert(1)</script> vào text fields (XSS)
  • Submit form rỗng hoàn toàn
  • Double-click button submit (duplicate submission)
  • Copy-paste thay vì gõ tay vào password field
  • Để form idle 30 phút rồi submit (session timeout)
  • Nhập số âm vào quantity field

6. Use Case / User Journey Testing

Test theo luồng thực tế người dùng sẽ đi qua, không phải test từng feature riêng lẻ. Bắt các integration bugs.

Ví dụ E2E: Search sản phẩm → Xem chi tiết → Add to cart → Checkout → Payment → Nhận email xác nhận → Kiểm tra order history

Lưu ý
Trong thực tế, dùng kết hợp nhiều kỹ thuật. BVA luôn đi kèm EP. Decision Table dùng khi có 3+ conditions. Error Guessing bổ sung sau khi đã dùng systematic techniques.

🗄️Test Data Management

Test data kém là nguyên nhân hàng đầu khiến test cases flaky và không reproducible. Quản lý test data tốt là dấu hiệu của một QA team trưởng thành.

Static vs Dynamic Test Data

LoạiMô tảƯu điểmNhược điểmDùng khi nào
Static Data Data được tạo trước và giữ cố định trong DB Predictable, reproducible, nhanh setup Có thể bị contaminate giữa test runs, cần reset Reference data: country lists, product catalog, user roles
Dynamic Data Data được tạo on-the-fly mỗi lần chạy test Luôn fresh, không bị polluted, test isolation tốt Chậm hơn, cần cleanup sau test User accounts, orders, transactions, unique IDs

Cách Tạo Test Data

  • Manual creation: Tạo tay qua UI hoặc trực tiếp trong DB. Dùng cho small-scale testing hoặc khi data phức tạp.
  • SQL scripts: Viết INSERT scripts để seed data. Luôn có cleanup script tương ứng. Lưu trong version control cùng code.
  • Faker libraries: Dùng Faker.js, Python Faker, Bogus (.NET) để generate realistic fake data tự động.
  • Snapshot từ production: Anonymize rồi import subset của production data. Đảm bảo compliance trước khi làm.
  • Test data factory: Code pattern tạo objects với sensible defaults, chỉ override field cần test.

Data Masking cho PII

Compliance — quan trọng
Không bao giờ dùng real production data (tên, email, phone, CCCD, thẻ tín dụng) trong test environment nếu chưa được anonymize. Vi phạm GDPR/PDPA có thể dẫn đến phạt nặng và mất uy tín công ty.

Các kỹ thuật masking:

  • Substitution: Thay real name bằng fake name từ library (John Smith → Test User 001)
  • Shuffling: Xáo trộn giá trị trong cùng column (email của user A đổi sang user B và ngược lại)
  • Encryption: Mã hoá field nhưng giữ format (email → mã hoá nhưng vẫn có @domain.com)
  • Nullification: Set về NULL hoặc default value cho fields không cần test

Lưu Trữ và Quản Lý

  • Lưu test data scripts trong Git cùng với test cases
  • Có README mô tả cách setup và reset data
  • Separate data theo environment: dev, staging, UAT
  • Document test accounts trong team wiki (không để trong code)
  • Đặt lịch cleanup data cũ định kỳ (weekly/monthly)

👁️Test Case Review Process

Review là bước tạo ra quality trước khi chạy test. Một test case review tốt có thể catch 20-30% defects trong chính test cases.

Checklist Review

  • Title có mô tả rõ "test gì" không? Đọc xong có hiểu ngay không?
  • Preconditions đầy đủ và cụ thể chưa? Người khác có thể setup được không?
  • Test steps có đủ chi tiết? Có bước nào mơ hồ không?
  • Expected result có measurable và specific không, hay vẫn còn mơ hồ?
  • Test data được chỉ rõ chưa? Có dùng real PII không?
  • TC có link đến requirement chưa (traceability)?
  • Priority có phù hợp với business impact không?
  • Có cover negative cases không chỉ positive?
  • Có overlap với TC khác không (duplicate effort)?
  • Edge cases và boundary values đã cover chưa?

Peer Review vs QA Lead Review

AspectPeer ReviewQA Lead Review
ReviewerQA engineer cùng levelSenior QA / QA Manager
FocusCorrectness, completeness, clarityCoverage, strategy, risk alignment
TimingDraft hoàn thành, trước executionSau peer review, trước sprint start
OutcomeApprove / Request changesFinal sign-off, prioritization
FrequencyMọi TC mới hoặc major updatePer feature / per sprint

Common Issues Tìm Thấy Trong Review

  • Vague expected results: "hệ thống hiển thị thông báo" — thông báo gì? ở đâu?
  • Missing negative tests: Chỉ có happy path, không test invalid inputs
  • Unclear preconditions: "User đã login" — login với role gì? data gì?
  • Steps too coarse: Một step làm nhiều việc, khó biết step nào fail
  • No traceability: Không link req, không biết test cái này để làm gì
  • Wrong priority: Bug cosmetic P1, critical business flow P3

🔧Test Case Maintenance

Test cases không phải write once — chúng cần được cập nhật liên tục theo evolution của product. Obsolete test cases là gánh nặng, không phải tài sản.

Khi Nào Cần Update Test Case

  • Requirement thay đổi (feature change, new business rule)
  • UI thay đổi (element bị rename, moved, redesigned)
  • Bug fix làm thay đổi behavior của system
  • Test case failed vì precondition không còn valid
  • Test data không còn tồn tại hoặc outdated
  • Test case tìm thấy gap mới trong quá trình execute

Version Control cho Test Cases

Trong các tool như TestRail, Zephyr, hoặc Xray, test cases có built-in versioning. Nếu dùng Excel/Google Sheet:

  • Thêm cột "Version" và "Last Updated Date"
  • Thêm cột "Change Log" ghi lý do update
  • Lưu file trong Git hoặc SharePoint với version history
  • Không xoá test case cũ — chuyển sang "Deprecated" status

Xử Lý Obsolete Test Cases

Đừng xoá ngay — nhiều công ty cần audit trail. Thay vào đó:

  • Đánh dấu status "Obsolete" hoặc "Deprecated"
  • Add comment lý do obsolete và ngày
  • Link đến requirement change hoặc ticket làm nó obsolete
  • Move vào folder "Archive" để tránh nhầm lẫn
  • Sau 6 tháng, review lại — nếu vẫn không dùng thì xoá hẳn

⚠️8 Lỗi Phổ Biến Junior QA

Những lỗi này xuất hiện trong review của tôi hàng tuần. Đọc kỹ và tự kiểm tra lại test cases của bạn.

#LỗiVí dụ xấuCách khắc phục
1 Expected result quá chung chung "User đăng nhập thành công" Specify: "Redirect sang /dashboard, hiển thị 'Welcome [name]', URL không còn /login"
2 Không test negative cases Chỉ test đăng nhập đúng Tạo test case cho: sai password, email không tồn tại, account locked, blank fields
3 Test steps quá gộp "Step 1: Mở app, login, vào profile, update avatar" Mỗi step = 1 action. Tách thành 4 steps riêng biệt
4 Preconditions mơ hồ "Precondition: có user trong hệ thống" "User 'qa.test@example.com' / 'Pass@123' tồn tại, role Admin, email verified"
5 Copy-paste test data trong steps Mỗi step ghi lại toàn bộ data Tách ra section Test Data riêng, steps chỉ reference "sử dụng data từ Test Data section"
6 Không có traceability Test case không link req nào Mỗi TC phải link ít nhất 1 requirement/user story. Nếu không có → hỏi lại BA/PM
7 Title không mô tả rõ "TC-001: Test login" Format: "[Subject] [action] [condition] → [expected]". Ví dụ: "Login fails when password has less than 8 characters"
8 Ignore boundary values Field cho phép 1-100: chỉ test 50 Luôn test: 0, 1, 2, 99, 100, 101, -1, max int, empty string
Senior Insight — Điều gì khác biệt Senior QA

Senior QA không chỉ viết test case "đúng" — họ viết test case "đúng chỗ". Đó là Risk-Based Prioritization: phân tích xem feature nào có nguy cơ fail cao nhất, ảnh hưởng lớn nhất đến user/business, và focus effort vào đó.

Senior hiểu tradeoff giữa coverage và time. Không có đủ thời gian để test 100% mọi thứ — Senior biết cắt đúng chỗ. Họ hỏi: "Nếu tôi chỉ có 4 giờ để test feature này trước release, tôi sẽ test gì?" và câu trả lời đó reflect hiểu biết về business risk, technical complexity và historical defects.

Junior viết nhiều TC nhưng miss critical paths. Senior viết ít hơn nhưng catch nhiều bug hơn vì họ test đúng chỗ.

✏️Bài Tập Thực Hành

Bài tập — Tự làm

Viết test cases đầy đủ cho feature "Đăng ký tài khoản"

Yêu cầu business:

  • Username: 3-20 ký tự, chỉ cho phép a-z, 0-9, underscore. Phải unique trong hệ thống.
  • Email: format hợp lệ, phải unique, hệ thống gửi verification email.
  • Password: tối thiểu 8 ký tự, phải có ít nhất 1 chữ hoa và 1 số.
  • Confirm Password: phải khớp với Password.
  • Sau đăng ký thành công: hiển thị "Vui lòng kiểm tra email để xác thực tài khoản."

Nhiệm vụ:

  1. Xác định tất cả equivalence partitions cho mỗi field
  2. Xác định boundary values cho username và password length
  3. Tạo decision table cho tất cả error scenarios
  4. Viết ít nhất 15 test cases (positive + negative + edge cases) theo format chuẩn 9 thành phần
  5. Assign priority cho mỗi TC và giải thích tại sao

💬Interview Questions

Test case và test scenario khác nhau như thế nào?
Test scenario là high-level description của "cái gì cần test" (What to test), ví dụ: "Verify user can login". Test case là low-level, step-by-step instructions "test như thế nào" (How to test), bao gồm specific data, steps và expected results. Một scenario có thể có nhiều test cases (positive, negative, edge cases).
Khi không có requirement document, bạn sẽ viết test case như thế nào?
Đầu tiên tôi sẽ cố gắng thu thập thông tin từ nhiều nguồn: talk với BA/PM để hiểu business intent, xem UI mockups/wireframes, hỏi developer về implementation details, và review acceptance criteria trong JIRA nếu có. Sau đó tôi document lại assumptions, viết test cases dựa trên assumptions đó và request review từ stakeholders. Tôi cũng sẽ flag rõ đây là "assumption-based test cases" cần được confirmed.
Làm sao để quyết định bao nhiêu test cases là đủ cho một feature?
Không có con số tuyệt đối. Tôi dùng risk-based approach: tính toán risk = probability of failure × business impact. Feature có high risk cần coverage cao hơn. Tôi đảm bảo đủ: (1) tất cả happy paths quan trọng, (2) tất cả error states có thể xảy ra, (3) boundary values, (4) cross-field validation, (5) integration points. Coverage ≈ 80% là realistic target cho sprint delivery.
Bạn sẽ làm gì nếu test case không pass nhưng bạn không chắc là bug hay expected behavior?
Tôi sẽ: (1) Re-read requirement cẩn thận để verify, (2) Re-run test để loại trừ fluke, (3) Test thêm variations để hiểu rõ behavior, (4) Hỏi developer về implementation intent, (5) Nếu vẫn không rõ → escalate lên BA/PM để clarify. Tôi sẽ log bug với status "Needs Discussion" và document full investigation findings. Quan trọng là không assume — luôn verify.
Equivalence Partitioning và Boundary Value Analysis — khi nào dùng cái nào?
Thực tế dùng cả hai kết hợp. EP xác định các nhóm input có behavior giống nhau, giúp reduce số lượng test cases. BVA focus vào các giá trị tại ranh giới giữa các nhóm — nơi bugs hay xuất hiện nhất (off-by-one errors). Workflow: dùng EP để identify partitions, sau đó áp dụng BVA tại boundaries giữa các partitions. Ví dụ: EP cho ra partitions [<18, 18-65, >65], BVA test tại 17, 18, 19, 64, 65, 66.