Menu
ESC

Nhập từ khóa để tìm kiếm

↑↓ Di chuyển
Enter Mở
ESC Đóng

Đang tải...

Acceptance Criteria Writing cho Tester

Software Testing Fundamentals Bài 40/60

Mở đầu — vì sao bài này quan trọng

Hãy tưởng tượng bạn nhận được một user story trên Jira với nội dung vỏn vẹn: "Là người dùng, tôi muốn đăng nhập vào hệ thống." Dev đọc xong gật gù code trong hai ngày. Đến lúc bạn — tester — cầm feature lên để test, bạn bắt đầu hỏi: Đăng nhập bằng email hay số điện thoại? Sai mật khẩu 5 lần thì có bị khóa không? Có nhớ đăng nhập không? Nhập sai định dạng email thì báo lỗi thế nào? Và bạn phát hiện ra: không ai biết câu trả lời, kể cả người viết story. Kết quả là một mớ tranh cãi kéo dài, bug qua lại, và feature bị delay cả sprint.

Câu chuyện này lặp đi lặp lại ở hầu hết đội phát triển phần mềm mà tôi từng làm việc. Gốc rễ của nó không phải là dev kém hay tester lười, mà là thiếu Acceptance Criteria (AC) rõ ràng. AC — tiêu chí chấp nhận — là bộ điều kiện xác định khi nào một user story được coi là "hoàn thành đúng ý". Nó là ranh giới giữa "code chạy được" và "code làm đúng thứ khách hàng cần".

Nhiều tester nghĩ rằng viết AC là việc của Business Analyst (BA) hoặc Product Owner (PO). Điều đó chỉ đúng một nửa. Trong đội Agile hiện đại, tester là người đọc AC để thiết kế test, review AC để phát hiện lỗ hổng từ sớm, và không ít lần trực tiếp viết hoặc bổ sung AC khi BA bỏ sót. Một tester biết viết AC tốt sẽ tìm ra bug trước khi dòng code đầu tiên được viết ra — đó là loại phòng ngừa (defect prevention) có giá trị gấp nhiều lần việc tìm bug sau khi đã code xong. Bài học này sẽ trang bị cho bạn đúng kỹ năng đó.

Khái niệm cốt lõi

Acceptance Criteria là gì

Acceptance Criteria là tập hợp các điều kiện cụ thể, có thể kiểm chứng được mà một sản phẩm hoặc tính năng phải thỏa mãn để được PO chấp nhận. Nói cách khác, AC trả lời câu hỏi: "Làm sao chúng ta biết story này đã done và done đúng?"

Đặc điểm của một AC tốt:

  • Cụ thể (specific): không mơ hồ kiểu "hệ thống phải nhanh" mà phải "trang tải xong trong 3 giây với mạng 4G".
  • Kiểm chứng được (testable): mỗi tiêu chí phải map được thành ít nhất một test case có kết quả pass/fail rõ ràng.
  • Ngắn gọn, dễ hiểu: viết cho cả người không kỹ thuật đọc được.
  • Tập trung vào "cái gì", không phải "làm thế nào": AC mô tả hành vi mong muốn, không quy định cách hiện thực. "Người dùng nhận được email xác nhận" là AC; "gửi email qua SendGrid với template X" là chi tiết kỹ thuật, không thuộc AC.

Phân biệt AC với các khái niệm dễ nhầm

Đây là chỗ nhiều bạn junior nhầm lẫn, nên tôi tách rõ:

  • AC vs Definition of Done (DoD): AC riêng cho từng story, mô tả hành vi cụ thể của story đó. DoD là bộ tiêu chuẩn chung áp cho mọi story (ví dụ: code đã review, đã có unit test, đã pass CI, đã cập nhật tài liệu). Một story done khi thỏa mãn cả AC riêng của nó DoD chung.
  • AC vs Requirement: Requirement là mô tả nhu cầu ở mức cao. AC là sự cụ thể hóa requirement thành điều kiện đo được cho một story.
  • AC vs Test Case: AC nói "cái gì phải đúng"; test case nói "làm những bước gì để kiểm tra". Một AC thường sinh ra nhiều test case (positive, negative, edge case).

Hai định dạng viết AC phổ biến

1. Định dạng Given/When/Then (Gherkin)

Đây là định dạng được ưa chuộng trong BDD (Behavior-Driven Development). Cấu trúc gồm ba phần:

  • Given (Cho trước): bối cảnh, tiền điều kiện — hệ thống đang ở trạng thái nào.
  • When (Khi): hành động người dùng thực hiện.
  • Then (Thì): kết quả mong đợi có thể quan sát được.
Feature: Đăng nhập bằng email

Scenario: Đăng nhập thành công với thông tin hợp lệ Given người dùng đã có tài khoản với email "an@example.com" And đang ở trang đăng nhập When người dùng nhập email "an@example.com" và mật khẩu đúng And nhấn nút "Đăng nhập" Then người dùng được chuyển đến trang chủ And thấy lời chào "Xin chào An"

Scenario: Khóa tài khoản sau 5 lần sai mật khẩu Given người dùng đã nhập sai mật khẩu 4 lần liên tiếp When người dùng nhập sai mật khẩu lần thứ 5 Then tài khoản bị khóa tạm thời trong 15 phút And hiển thị thông báo "Tài khoản tạm khóa, thử lại sau 15 phút"

Ưu điểm của Gherkin: mô tả rành mạch từng kịch bản, ai đọc cũng hiểu, và có thể chuyển thẳng thành automation test bằng Cucumber, SpecFlow, hay Behave. Với tester, mỗi Scenario gần như là một test case đã được viết sẵn.

2. Định dạng Checklist (Rule-oriented)

Khi story có nhiều ràng buộc rời rạc mà không dễ diễn tả thành kịch bản, checklist gọn hơn:

- Email phải đúng định dạng RFC 5322, nếu sai hiển thị "Email không hợp lệ".
  • Mật khẩu tối thiểu 8 ký tự, có ít nhất 1 chữ hoa và 1 số.
  • Sau 5 lần đăng nhập sai, khóa tài khoản 15 phút.
  • Có checkbox "Ghi nhớ đăng nhập" giữ phiên 30 ngày.
  • Hỗ trợ đăng nhập trên Chrome, Safari, Edge phiên bản mới nhất.

Lời khuyên của tôi: dùng Gherkin cho hành vi có luồng rõ ràng (đăng nhập, thanh toán, đặt hàng), dùng checklist cho các ràng buộc dữ liệu và validation. Nhiều story thực tế trộn cả hai.

Tình huống thực tế

Ví dụ 1 — Tiki và story "Áp mã giảm giá" bị hiểu sai

Một đội thương mại điện tử (giả định theo bối cảnh Tiki) nhận story: "Là khách hàng, tôi muốn áp mã giảm giá khi thanh toán để tiết kiệm tiền." PO chỉ viết đúng một dòng đó. Dev hiểu là "cho phép nhập mã và trừ tiền", và họ code đúng như vậy.

Khi lên production, phát sinh hàng loạt sự cố: khách áp được hai mã cùng lúc (mã freeship + mã giảm 50k) khiến đơn hàng bị lỗ; mã đã hết hạn vẫn dùng được; mã dành cho khách VIP bị khách thường áp trót lọt. Trong một chiến dịch sale lớn, chỉ riêng lỗ hổng chồng mã đã gây thiệt hại ước tính hàng chục triệu đồng trong vài giờ trước khi bị chặn khẩn cấp.

Nếu AC được viết đầy đủ ngay từ đầu, những dòng như sau đã ngăn được thảm họa:

Scenario: Không cho áp hai mã giảm giá cùng lúc
  Given giỏ hàng đã áp mã "FREESHIP"
  When khách hàng nhập tiếp mã "GIAM50K"
  Then hệ thống báo "Chỉ được áp một mã mỗi đơn hàng"
  And tổng tiền không thay đổi

Scenario: Mã hết hạn Given mã "TET2025" đã quá hạn sử dụng When khách hàng nhập mã "TET2025" Then hệ thống báo "Mã đã hết hạn"

Bài học rút ra: AC là nơi bắt các "luật kinh doanh ngầm" (implicit business rules). Tester đọc story mà thấy thiếu các điều kiện negative như hết hạn, chồng mã, sai đối tượng — đó chính là lúc phải giơ tay yêu cầu bổ sung AC, trước khi sprint bắt đầu.

Ví dụ 2 — Fintech VN và AC cho luồng nạp tiền ví

Một startup ví điện tử tại TP.HCM (giả định tương tự MoMo giai đoạn đầu) có story: "Người dùng nạp tiền vào ví từ tài khoản ngân hàng." Tester trong đội — bạn Linh — được mời vào buổi refinement. Thay vì ngồi im chờ dev code, Linh đặt câu hỏi và cùng BA viết AC dưới dạng bảng tình huống:

Scenario: Nạp tiền thành công
  Given số dư ví là 100.000đ
  When người dùng nạp 50.000đ từ ngân hàng Vietcombank thành công
  Then số dư ví là 150.000đ
  And người dùng nhận thông báo push "Nạp thành công 50.000đ"

Scenario: Ngân hàng trừ tiền nhưng ví chưa cộng (giao dịch treo) Given người dùng đã bị trừ 50.000đ ở ngân hàng But webhook xác nhận chưa về sau 60 giây Then giao dịch chuyển trạng thái "Đang xử lý" And hệ thống tự đối soát lại trong vòng 24 giờ And số tiền không bị mất

Chính cái Scenario thứ hai — trường hợp "giao dịch treo" — là thứ mà dev ban đầu hoàn toàn không nghĩ tới. Nhờ Linh nêu ra ở giai đoạn viết AC, đội đã thiết kế cơ chế đối soát (reconciliation) ngay từ đầu, thay vì phải vá lỗi sau khi khách hàng mất tiền và lên mạng phàn nàn.

Bài học rút ra: Với các nghiệp vụ liên quan tiền bạc, AC phải bao gồm cả kịch bản thất bại một phần (partial failure) và trạng thái trung gian. Tester là người có tư duy "chuyện gì xảy ra nếu bước giữa chừng đứt" tốt nhất trong đội — hãy tận dụng nó khi viết AC.

Ví dụ 3 — Story quá "to" và cách chẻ AC

Một đội SaaS làm phần mềm quản lý nhà hàng nhận story: "Quản lý muốn xem báo cáo doanh thu." Khi ngồi viết AC, tester nhận ra story này khổng lồ: báo cáo theo ngày/tuần/tháng, lọc theo chi nhánh, xuất Excel, biểu đồ, so sánh kỳ trước... Không thể viết AC gọn cho một story ôm đồm như vậy.

Tester đề xuất chẻ nhỏ (split) và mỗi story con có AC riêng, tập trung:

  • Story A — "Xem doanh thu theo ngày": AC chỉ xoay quanh chọn ngày, hiển thị tổng, và trường hợp ngày không có giao dịch (hiển thị "0đ", không lỗi).
  • Story B — "Lọc doanh thu theo chi nhánh".
  • Story C — "Xuất báo cáo ra Excel".
Bài học rút ra: Nếu bạn viết AC mà thấy nó dài lê thê, có hơn 10 tiêu chí không liên quan nhau, đó là tín hiệu story quá lớn. AC tốt còn là công cụ giúp đội nhận ra story cần được split — một đóng góp rất được PO trân trọng.

Hướng dẫn từng bước

Đây là quy trình tôi khuyên tester áp dụng khi làm việc với AC trong một sprint:

Bước 1 — Đọc story và xác định "actor" và "mục tiêu". Với mỗi story dạng "Là [ai], tôi muốn [gì], để [đạt được gì]", xác định rõ người dùng là ai và họ đang cố làm gì. Mọi AC phải phục vụ mục tiêu này.

Bước 2 — Liệt kê các kịch bản happy path. Viết trước các luồng thành công cơ bản dưới dạng Given/When/Then. Đây là phần dễ, ai cũng nghĩ ra.

Bước 3 — Săn tìm negative và edge case. Đây là nơi tester tỏa sáng. Tự hỏi: Dữ liệu rỗng thì sao? Nhập sai định dạng? Vượt giới hạn (số âm, quá dài)? Mất mạng giữa chừng? Người dùng không có quyền? Hai người thao tác cùng lúc? Mỗi câu hỏi có giá trị thường là một AC mới.

Bước 4 — Thêm ràng buộc phi chức năng nếu liên quan. Hiệu năng ("tải trong 3 giây"), bảo mật ("không hiển thị mật khẩu dạng plaintext"), khả năng truy cập, hỗ trợ trình duyệt/thiết bị. Chỉ thêm khi story thực sự có yêu cầu này, đừng nhồi nhét.

Bước 5 — Kiểm tra tính testable của từng dòng. Đọc lại mỗi AC và tự hỏi: "Tôi có viết được test case pass/fail cho dòng này không?" Nếu câu trả lời là không (ví dụ "giao diện phải đẹp"), viết lại cho đo được ("nút CTA có màu chủ đạo #FF5722, cao tối thiểu 44px để bấm được trên mobile").

Bước 6 — Review cùng đội trong buổi refinement/grooming. Đọc to AC cho dev và PO nghe. Đây là lúc bắt được hiểu lầm. Câu thần chú của tôi: "Nếu tester, dev và PO đọc cùng một AC mà tưởng tượng ra ba sản phẩm khác nhau, thì AC đó chưa xong."

Bước 7 — Chốt AC trước khi story vào sprint. AC nên được finalize trước khi dev bắt đầu code (thường ở refinement của sprint trước). AC thay đổi giữa sprint là nguồn gốc của scope creep và bug.

Lỗi thường gặp & mẹo

Lỗi 1 — Viết AC mô tả cách hiện thực thay vì hành vi. Sai: "Lưu email vào bảng users bằng câu INSERT." Đúng: "Sau khi đăng ký, người dùng có thể đăng nhập bằng email vừa tạo." AC nói what, không nói how.

Lỗi 2 — AC không kiểm chứng được. Những từ như "nhanh", "dễ dùng", "thân thiện", "ổn định" là cờ đỏ. Luôn quy chúng về con số hoặc hành vi quan sát được.

Lỗi 3 — Chỉ viết happy path. Đây là lỗi phổ biến nhất của người mới. Một story đăng nhập chỉ có AC "nhập đúng thì vào được" là thiếu ít nhất 5-6 kịch bản (sai mật khẩu, khóa tài khoản, quên mật khẩu, email chưa xác thực...). Kinh nghiệm: với mỗi happy path, ép mình nghĩ ra ít nhất hai đường thất bại.

Lỗi 4 — Nhồi nhiều hành vi vào một AC. Mỗi Scenario nên kiểm một hành vi. Nếu một Then có ba mệnh đề "và" không liên quan, cân nhắc tách ra để khi fail bạn biết chính xác cái gì hỏng.

Lỗi 5 — Nhầm AC với DoD. Đừng đưa "code đã review", "đã có unit test" vào AC của một story — đó là DoD chung của cả đội, không phải hành vi riêng của story.

Mẹo hay:

  • Ba câu hỏi vàng cho mọi story: (1) Cái gì có thể rỗng? (2) Cái gì có thể sai định dạng? (3) Cái gì xảy ra khi thao tác thất bại giữa chừng? Trả lời ba câu này là bạn đã có phần lớn AC negative.
  • Đọc AC ngược từ dưới lên: đọc phần Then trước rồi tự hỏi "cần Given/When gì để dẫn tới kết quả này" — giúp phát hiện tiền điều kiện bị thiếu.
  • Dùng ngôn ngữ nghiệp vụ, không dùng thuật ngữ kỹ thuật trong AC, để PO và khách hàng đọc được và xác nhận.
  • Số hóa mọi thứ có thể: thay "mật khẩu đủ mạnh" bằng "tối thiểu 8 ký tự, 1 chữ hoa, 1 số".

Bài tập thực hành

Bài 1 — Viết AC dạng Gherkin. Cho story: "Là người dùng, tôi muốn đặt lại mật khẩu qua email khi quên mật khẩu." Hãy viết ít nhất 4 Scenario Given/When/Then, trong đó tối thiểu 2 Scenario là negative/edge case (gợi ý: email không tồn tại trong hệ thống, link đặt lại đã hết hạn, người dùng nhập mật khẩu mới trùng mật khẩu cũ).

Bài 2 — Sửa AC kém chất lượng. Cho các AC sau, hãy chỉ ra vấn đề và viết lại cho testable:

  • "Trang tìm kiếm phải nhanh và mượt."
  • "Hệ thống xử lý được nhiều người dùng."
  • "Giao diện thanh toán phải chuyên nghiệp."
Bài 3 — Chẻ story. Cho story lớn: "Là admin, tôi muốn quản lý người dùng." Hãy chẻ thành ít nhất 3 story con và viết 2-3 AC cho một trong số đó.

Bài 4 — Tình huống VN. Viết AC (dạng checklist hoặc Gherkin) cho story: "Người dùng nhập họ tên tiếng Việt có dấu khi đăng ký." Nghĩ tới các trường hợp: tên có dấu ("Nguyễn Thị Bích Hằng"), tên rất dài, tên chứa ký tự đặc biệt, hiển thị lại đúng dấu ở trang hồ sơ.

Gợi ý tự chấm: một bộ AC tốt phải có cả positive lẫn negative, mỗi dòng đều viết được test case pass/fail, và một người ngoài đội đọc vẫn hiểu.

Tóm tắt

Acceptance Criteria là bộ điều kiện cụ thể, kiểm chứng được xác định khi nào một user story được coi là hoàn thành đúng ý. Với tester, AC không chỉ là tài liệu để đọc mà là công cụ phòng ngừa bug từ sớm: đọc AC để thiết kế test, review AC để bắt lỗ hổng trước khi code, và nhiều khi trực tiếp viết bổ sung AC.

Hai định dạng chính là Given/When/Then (Gherkin) cho các hành vi có luồng rõ ràng, và checklist cho các ràng buộc dữ liệu, validation. Một AC tốt phải cụ thể, testable, tập trung vào cái gì chứ không phải làm thế nào, và không lẫn lộn với Definition of Done.

Sức mạnh thực sự của tester nằm ở khả năng săn tìm negative và edge case — những kịch bản thất bại, dữ liệu rỗng, sai định dạng, giao dịch treo — mà các ví dụ về mã giảm giá và nạp tiền ví đã cho thấy chúng đắt giá đến mức nào. Hãy nhớ ba câu hỏi vàng: cái gì có thể rỗng, cái gì có thể sai định dạng, cái gì xảy ra khi thất bại giữa chừng. Nắm chắc AC, bạn sẽ dịch chuyển vai trò của mình từ "người tìm bug" sang "người ngăn bug" — và đó chính là bước tiến lớn nhất trong sự nghiệp QA.