Menu
ESC

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

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

Đang tải...

Bài 21 — Acceptance Criteria: Gherkin sample

Viết PRD & Product Spec cho PM Bài 21/60

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

Hãy tưởng tượng bạn vừa viết một dòng acceptance criteria thế này: "Người dùng có thể thêm sản phẩm vào giỏ hàng từ livestream." Nghe có vẻ rõ ràng đúng không? Nhưng khi bạn đưa cho ba người khác nhau đọc — một developer, một QA, một designer — họ sẽ hình dung ra ba thứ khác nhau. Developer băn khoăn: thêm xong thì giỏ hàng cập nhật ngay hay đợi reload? QA hỏi: nếu sản phẩm hết hàng giữa lúc đang xem live thì sao? Designer thắc mắc: bấm "Thêm" xong người dùng có bị đưa ra khỏi màn hình live không?

Một câu mô tả mơ hồ như vậy sẽ sinh ra hàng loạt cuộc họp, hàng loạt tin nhắn "anh ơi cái này ý là sao", và tệ nhất là sản phẩm ship ra không đúng ý. Đây chính là lý do tồn tại của Gherkin — một cú pháp viết acceptance criteria (tiêu chí nghiệm thu) theo cấu trúc Given–When–Then. Gherkin không phải là phát minh để làm khó PM; nó là ngôn ngữ chung giúp PM, dev, QA và cả người không kỹ thuật cùng đọc một câu và hiểu giống hệt nhau.

Ở Bài 4, bạn đã học user stories và acceptance criteria ở mức khái niệm. Bài này đi sâu vào một định dạng cụ thể, có kỷ luật để viết acceptance criteria sao cho không thể hiểu nhầm, và quan trọng hơn — viết để QA có thể biến thẳng thành test case. Đây là kỹ năng phân biệt một PM viết spec "tàm tạm" với một PM mà cả team tin tưởng.

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

Gherkin là gì

Gherkin là một cú pháp gần với ngôn ngữ tự nhiên, dùng để mô tả hành vi mong muốn của hệ thống theo từng kịch bản (scenario). Nó ra đời từ phong trào BDD (Behavior-Driven Development — phát triển dựa trên hành vi) và được dùng phổ biến trong các công cụ như Cucumber. Nhưng bạn không cần biết Cucumber để dùng Gherkin: rất nhiều PM dùng nó thuần túy như một cách viết acceptance criteria trong PRD.

Cấu trúc xương sống của Gherkin chỉ có ba từ khóa:

Given [tiền điều kiện — bối cảnh trước khi hành động]
When  [hành động — điều người dùng/hệ thống làm]
Then  [kết quả mong đợi — điều phải xảy ra sau đó]
  • Given: trạng thái hệ thống trước khi sự việc xảy ra. Ví dụ: "người dùng đã đăng nhập", "giỏ hàng đang trống", "sản phẩm còn 2 cái trong kho".
  • When: sự kiện kích hoạt — thường là một hành động duy nhất của người dùng. Ví dụ: "người dùng bấm nút Thêm vào giỏ".
  • Then: kết quả có thể quan sát được. Ví dụ: "giỏ hàng hiển thị số lượng là 1", "hệ thống hiện toast 'Đã thêm vào giỏ'".

And, But và cách nối nhiều điều kiện

Khi một bước cần nhiều điều kiện, bạn dùng And (và) hoặc But (nhưng) để nối, thay vì lặp lại Given/When/Then:

Given người dùng đã đăng nhập
And giỏ hàng đang trống
When người dùng bấm "Thêm vào giỏ" trên sản phẩm A
Then giỏ hàng hiển thị số lượng 1
And badge giỏ hàng đổi sang màu đỏ
But người dùng vẫn ở lại màn hình livestream

Đọc lên gần như một câu tiếng Việt mạch lạc. Đó chính là sức mạnh: ai cũng đọc được, nhưng cấu trúc lại đủ chặt để không mơ hồ.

Scenario và Scenario Outline

Mỗi kịch bản cụ thể được gói trong một Scenario có tên. Tên scenario nên mô tả tình huống, không phải mô tả tính năng:

Scenario: Thêm sản phẩm còn hàng vào giỏ từ livestream
Scenario: Thêm sản phẩm vừa hết hàng vào giỏ từ livestream
Scenario: Thêm sản phẩm khi chưa đăng nhập

Khi nhiều scenario chỉ khác nhau ở dữ liệu đầu vào, bạn dùng Scenario Outline kèm bảng Examples để tránh lặp:

Scenario Outline: Áp mã giảm giá theo giá trị đơn hàng
  Given giỏ hàng có tổng giá trị <tong_tien>
  When người dùng nhập mã "<ma>"
  Then hệ thống áp giảm <giam> đồng

Examples:

tong_tienmagiam
500000FREESHIP25000
200000FREESHIP
1000000SALE10100000

Một dòng spec gọn gàng thay cho ba scenario riêng lẻ. QA nhìn vào bảng này là viết được ngay ba test case.

Tại sao một AC tốt phải "kiểm chứng được"

Nguyên tắc vàng: mỗi mệnh đề Then phải mô tả thứ quan sát được hoặc đo được. "Hệ thống xử lý đúng" không phải AC — vì không ai biết "đúng" trông như thế nào. "Hệ thống hiển thị thông báo 'Sản phẩm đã hết hàng' và nút Thêm chuyển sang trạng thái mờ" mới là AC — vì QA mở app lên là nhìn thấy được, đúng thì pass, sai thì fail. Gherkin ép bạn viết theo kiểu này một cách tự nhiên.

Tình huống thực tế

Ví dụ 1 — Shopee Live: "Add to cart from live"

Giả sử bạn là PM của tính năng mua hàng trong livestream trên một sàn TMĐT kiểu Shopee. Người dùng đang xem live, người bán ghim một sản phẩm, và người xem có thể bấm thêm thẳng vào giỏ mà không rời khỏi luồng video. Yêu cầu ban đầu từ stakeholder chỉ vỏn vẹn: "Cho phép thêm sản phẩm vào giỏ từ live."

Nếu để nguyên như vậy, team sẽ ship một phiên bản hời hợt. Hãy xem cách biến nó thành Gherkin đầy đủ:

Scenario: Thêm sản phẩm còn hàng vào giỏ mà không rời live
  Given người dùng đã đăng nhập
  And đang xem một phiên livestream
  And sản phẩm được ghim còn ít nhất 1 sản phẩm trong kho
  When người dùng bấm "Thêm vào giỏ" trên thẻ sản phẩm ghim
  Then sản phẩm được thêm vào giỏ với số lượng 1
  And badge giỏ hàng tăng thêm 1
  And video livestream tiếp tục phát không bị gián đoạn
  And hệ thống hiện toast "Đã thêm vào giỏ" trong 2 giây

Scenario: Thêm sản phẩm vừa hết hàng giữa lúc live Given người dùng đang xem livestream And sản phẩm được ghim đã hết hàng sau lần làm mới gần nhất When người dùng bấm "Thêm vào giỏ" Then hệ thống hiện thông báo "Sản phẩm đã hết hàng" And nút "Thêm vào giỏ" chuyển sang trạng thái vô hiệu hóa And giỏ hàng không thay đổi

Scenario: Thêm sản phẩm khi chưa đăng nhập Given người dùng chưa đăng nhập And đang xem livestream When người dùng bấm "Thêm vào giỏ" Then hệ thống mở màn hình đăng nhập dưới dạng bottom sheet And livestream vẫn phát ở chế độ thu nhỏ phía sau And sau khi đăng nhập thành công, sản phẩm tự động được thêm vào giỏ

Diễn giải: Chỉ với một yêu cầu một dòng, ta đã khai mở ra ba kịch bản: đường hạnh phúc (happy path), trạng thái hết hàng (một edge case kinh điển trong live vì kho biến động theo giây), và trạng thái chưa đăng nhập. Điều then chốt là chi tiết "video livestream tiếp tục phát không bị gián đoạn" — nếu không viết ra, rất có thể dev sẽ làm nút Thêm điều hướng người dùng sang trang giỏ hàng, phá vỡ toàn bộ trải nghiệm mua hàng trong live.

Bài học rút ra: Gherkin buộc bạn nghĩ tới bối cảnh (Given) trước khi nghĩ tới kết quả. Chính bước viết Given "sản phẩm đã hết hàng giữa live" mới làm lộ ra một kịch bản mà yêu cầu gốc hoàn toàn bỏ qua.

Ví dụ 2 — MoMo: chuyển tiền với số dư không đủ

Đổi sang bối cảnh ví điện tử kiểu MoMo. Bạn là PM của luồng chuyển tiền P2P (người gửi người). Đây là tính năng tiền-thật, nên acceptance criteria phải cực kỳ chặt — sai một ly là mất tiền của người dùng.

Scenario Outline: Chuyển tiền với các mức số dư khác nhau
  Given người dùng có số dư <so_du> đồng
  And đã liên kết ngân hàng thành công
  When người dùng chuyển <so_tien> đồng cho người nhận hợp lệ
  Then kết quả giao dịch là "<ket_qua>"
  And số dư còn lại là <con_lai> đồng

Examples:

so_duso_tienket_quacon_lai
500000200000Thành công300000
500000500000Thành công
100000200000Từ chối100000
50000Từ chối
Scenario: Chuyển tiền nhưng phiên xác thực OTP hết hạn Given người dùng đã nhập số tiền và bấm "Chuyển" And mã OTP đã được gửi 5 phút trước When người dùng nhập OTP sau khi mã đã hết hạn Then hệ thống hiện thông báo "Mã OTP đã hết hạn, vui lòng yêu cầu mã mới" And giao dịch chưa được thực hiện And số dư của người dùng không thay đổi

Diễn giải: Bảng Examples ở đây là vũ khí mạnh nhất. Bốn dòng phủ kín bốn ranh giới quan trọng: số dư dư, số dư đúng bằng (boundary), số dư thiếu, và số dư bằng 0. Đây là tư duy boundary testing (kiểm thử biên) được nhúng thẳng vào spec. Dòng "con_lai = 100000 khi bị từ chối" tưởng nhỏ nhưng cực quan trọng — nó khẳng định rằng giao dịch thất bại tuyệt đối không được trừ tiền, một bug nguy hiểm trong fintech.

Bài học rút ra: Với feature liên quan đến tiền, bảo mật hay dữ liệu nhạy cảm, mệnh đề Then phải bao gồm cả "điều gì KHÔNG được xảy ra" (số dư không đổi), chứ không chỉ điều xảy ra. Gherkin cho bạn không gian để nói rõ điều đó.

Ví dụ 3 — Tiki: tìm kiếm trả về 0 kết quả

Một edge case mà 90% PM mới quên: empty state của tìm kiếm. Bạn là PM tìm kiếm sản phẩm trên một sàn kiểu Tiki.

Scenario: Tìm kiếm có kết quả
  Given người dùng ở màn hình tìm kiếm
  When người dùng nhập "tai nghe bluetooth" và bấm Tìm
  Then hệ thống hiển thị danh sách sản phẩm khớp
  And hiển thị tổng số kết quả ở đầu trang

Scenario: Tìm kiếm không có kết quả nào Given người dùng ở màn hình tìm kiếm When người dùng nhập "asdfghjkl" và bấm Tìm Then hệ thống hiển thị trạng thái rỗng với thông điệp "Không tìm thấy sản phẩm nào" And gợi ý 4 từ khóa phổ biến để người dùng thử lại And không hiển thị lưới sản phẩm trống

Diễn giải: Scenario thứ hai chính là thứ làm nên khác biệt giữa một spec nghiệp dư và chuyên nghiệp. PM thiếu kinh nghiệm chỉ viết happy path. PM giỏi luôn tự hỏi: "Còn khi không có gì để hiển thị thì sao?" Gherkin, với cấu trúc một-scenario-một-tình-huống, khuyến khích bạn liệt kê các nhánh này thành từng khối riêng biệt, dễ rà soát.

Bài học rút ra: Mỗi feature đều có ít nhất ba trạng thái cần spec — có dữ liệu, không có dữ liệu, và lỗi. Gherkin giúp bạn không gộp chúng lại thành một câu mơ hồ.

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

Đây là quy trình thực dụng để viết acceptance criteria bằng Gherkin cho một feature bất kỳ.

Bước 1 — Bắt đầu từ user story. Lấy user story dạng "Là [vai trò], tôi muốn [hành động], để [giá trị]". Ví dụ: "Là người mua, tôi muốn thêm sản phẩm vào giỏ từ live, để không bỏ lỡ ưu đãi flash sale."

Bước 2 — Viết happy path trước. Đừng vội nghĩ edge case. Viết một scenario cho luồng thuận lợi nhất, đầy đủ Given–When–Then. Điều này định hình "hành vi mặc định" mà mọi người đồng thuận.

Bước 3 — Liệt kê các nhánh rẽ. Với mỗi Given, tự hỏi: điều kiện này có thể sai khác thế nào? (chưa đăng nhập, hết hàng, mất mạng). Với mỗi When, tự hỏi: hành động có thể thất bại ra sao? Mỗi câu trả lời thường là một scenario mới.

Bước 4 — Gom các scenario giống nhau về dữ liệu thành Scenario Outline. Nếu bạn thấy ba scenario chỉ khác con số, hãy chuyển sang dạng bảng Examples. Code spec gọn hơn, dễ bảo trì hơn.

Bước 5 — Kiểm tra từng Then có "quan sát được" không. Đọc lại mỗi mệnh đề Then và hỏi: "QA mở app lên, họ thấy/đo được điều này không?" Nếu câu trả lời mơ hồ, viết lại cụ thể hơn. Thay "xử lý đúng" bằng "hiển thị thông báo X" hoặc "trả về mã trạng thái Y".

Bước 6 — Đưa cho một dev và một QA đọc thử. Hỏi họ: "Có chỗ nào đọc xong vẫn phải đoán không?" Mỗi câu hỏi của họ là một lỗ hổng trong spec. Vá nó, lặp lại cho đến khi không còn câu hỏi.

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

Lỗi 1 — Một scenario nhồi nhiều When. Nếu bạn viết "When người dùng đăng nhập And thêm sản phẩm And thanh toán", bạn đang mô tả cả một quy trình chứ không phải một hành vi. Khi test fail, không ai biết bước nào hỏng. Mẹo: mỗi scenario chỉ nên có một When chính. Tách quy trình dài thành nhiều scenario.

Lỗi 2 — Then mô tả chi tiết kỹ thuật triển khai. Ví dụ: "Then hệ thống gọi API POST /cart và ghi vào bảng cart_items." Đây là cách làm, không phải hành vi quan sát được, và nó trói tay developer. Mẹo: Then nên mô tả thứ người dùng hoặc người kiểm thử nhìn thấy ("giỏ hàng tăng lên 1"), để dev tự do chọn cách triển khai. (Bài 18 sẽ nói sâu hơn về nguyên tắc tả hành vi trước kiến trúc.)

Lỗi 3 — Given chứa hành động thay vì trạng thái. "Given người dùng bấm đăng nhập" là sai — bấm là một hành động, thuộc về When. Given phải là trạng thái tĩnh: "Given người dùng đã đăng nhập". Mẹo: Given thường dùng thì hoàn thành ("đã..."), When dùng thì hiện tại của hành động ("bấm...").

Lỗi 4 — Quên các trạng thái phụ. Chỉ viết happy path. Mẹo: với mỗi feature, ép mình viết tối thiểu ba scenario — thành công, rỗng/không có dữ liệu, và lỗi. (Chi tiết về các state này nằm ở Bài 23.)

Lỗi 5 — Đặt tên scenario theo tính năng, không theo tình huống. "Scenario: Giỏ hàng" vô nghĩa. Mẹo: tên scenario nên đọc như một tình huống cụ thể: "Scenario: Thêm sản phẩm hết hàng vào giỏ". Khi danh sách scenario được liệt kê ra, người đọc lướt qua tên là nắm được độ phủ.

Mẹo bonus — Đừng Gherkin hóa mọi thứ. Gherkin tỏa sáng với các feature có logic phân nhánh rõ (điều kiện, trạng thái, ranh giới). Với những thay đổi thuần thẩm mỹ (đổi màu nút, chỉnh khoảng cách), một câu mô tả thường gọn hơn và phù hợp hơn. Hãy dùng đúng công cụ cho đúng việc.

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

Chọn một trong hai đề sau và viết acceptance criteria bằng Gherkin.

Đề A — Đặt món trên app giao đồ ăn (kiểu GrabFood/ShopeeFood). User story: "Là khách, tôi muốn áp mã giảm giá vào đơn hàng để tiết kiệm tiền." Hãy viết tối thiểu:

  • 1 Scenario happy path (mã hợp lệ, đơn đủ điều kiện).
  • 1 Scenario mã hết hạn.
  • 1 Scenario đơn chưa đạt giá trị tối thiểu để áp mã.
  • 1 Scenario Outline với bảng Examples cho ít nhất ba mức giá trị đơn hàng khác nhau.
Đề B — Đăng nhập bằng số điện thoại + OTP. User story: "Là người dùng mới, tôi muốn đăng nhập bằng số điện thoại và mã OTP." Hãy viết:
  • 1 Scenario happy path.
  • 1 Scenario nhập sai OTP 3 lần (hệ thống nên làm gì?).
  • 1 Scenario OTP hết hạn.
  • 1 Scenario số điện thoại sai định dạng.
Tiêu chí tự chấm: (1) Mỗi scenario chỉ có một When chính. (2) Mọi Then đều quan sát được, không có "xử lý đúng". (3) Given chỉ chứa trạng thái, không chứa hành động. (4) Có ít nhất một mệnh đề Then mô tả "điều KHÔNG được xảy ra" cho kịch bản lỗi. Đưa kết quả cho một người bạn làm dev đọc và đếm xem họ hỏi lại bao nhiêu câu — mục tiêu là không câu nào.

Tóm tắt

Gherkin với cấu trúc Given–When–Then là cách viết acceptance criteria có kỷ luật, giúp PM, dev và QA cùng đọc một câu và hiểu giống nhau. Given mô tả trạng thái trước hành động, When mô tả một hành động kích hoạt duy nhất, Then mô tả kết quả quan sát được. Dùng And/But để nối điều kiện, Scenario Outline + Examples để gom các trường hợp chỉ khác nhau ở dữ liệu.

Sức mạnh thật sự của Gherkin không nằm ở cú pháp, mà ở chỗ nó ép bạn tư duy đầy đủ: viết Given làm lộ ra các bối cảnh bạn quên (hết hàng, chưa đăng nhập, mất mạng); viết Then quan sát được ép bạn bỏ những từ mơ hồ như "xử lý đúng". Quy tắc sống còn: mỗi Then phải kiểm chứng được, mỗi scenario một tình huống, và luôn viết tối thiểu ba nhánh — thành công, rỗng, lỗi. Khi spec của bạn được viết kỹ đến mức QA biến thẳng thành test case mà không cần hỏi lại, đó là lúc bạn đã viết acceptance criteria ở đẳng cấp khiến cả team tin tưởng.