Use when specifying testable acceptance criteria for user stories. Use after user story drafted. Produces Given-When-Then scenarios, edge case coverage, and validation approach.
Define clear, testable acceptance criteria that ensure shared understanding between product, development, and QA. Criteria should be specific enough to test but flexible enough to allow implementation decisions.
Core principle: If you can't write a test for it, it's not a valid acceptance criterion.
acceptance_criteria:
story_reference: "[Story ID]"
story_title: "[Story title]"
preconditions:
- "[System state required before testing]"
scenarios:
happy_path:
- id: "AC-01"
title: "[Scenario name]"
given: "[Initial context]"
when: "[Action performed]"
then: "[Expected outcome]"
priority: "[Must | Should | Could]"
alternate_paths:
- id: "AC-02"
title: "[Alternate scenario]"
given: "[Different context]"
when: "[Same or different action]"
then: "[Different outcome]"
priority: "[Must | Should | Could]"
error_handling:
- id: "AC-03"
title: "[Error scenario]"
given: "[Context that causes error]"
when: "[Action attempted]"
then: "[Error handling behavior]"
priority: "[Must | Should | Could]"
edge_cases:
- id: "AC-04"
title: "[Boundary or edge case]"
given: "[Edge condition]"
when: "[Action at boundary]"
then: "[Expected behavior]"
priority: "[Should | Could]"
non_functional:
performance:
- "[Response time requirements]"
security:
- "[Security constraints]"
accessibility:
- "[A11y requirements]"
out_of_scope:
- "[Explicitly not covered]"
validation_approach:
automated: "[What can be automated]"
manual: "[What requires manual testing]"
coverage_assessment:
happy_path: "[Complete | Partial | Missing]"
error_handling: "[Complete | Partial | Missing]"
edge_cases: "[Complete | Partial | Missing]"
non_functional: "[Complete | Partial | Missing]"
Good: "Given a logged-in user with admin privileges and an existing product with ID 123" Bad: "Given the system is ready"
Good: "When the user clicks 'Delete' and confirms the deletion dialog" Bad: "When the user does the delete"
Good: "Then the product is removed from the list, a success message displays for 3 seconds, and the product count decreases by 1" Bad: "Then it works"
The expected, successful flow:
- id: "AC-01"
title: "Successful order submission"
given: "User has items in cart and valid payment method"
when: "User clicks 'Place Order'"
then: "Order is created, confirmation email sent, inventory updated"
Valid but non-primary flows:
- id: "AC-02"
title: "Order with promo code"
given: "User has items in cart and valid promo code"
when: "User applies promo code and places order"
then: "Discount applied, order total updated, order created"
How system handles failures:
- id: "AC-03"
title: "Payment declined"
given: "User has items in cart and payment method that will be declined"
when: "User attempts to place order"
then: "Error message displayed, order not created, user can retry with different payment"
Boundary conditions:
- id: "AC-04"
title: "Cart at maximum item limit"
given: "User has 99 items in cart (max is 100)"
when: "User adds one more item"
then: "Item added successfully, cart shows 100 items"
- id: "AC-05"
title: "Cart exceeds maximum"
given: "User has 100 items in cart"
when: "User attempts to add another item"
then: "Error message explains limit, item not added"
| Priority | Meaning | Release Impact |
|---|---|---|
| Must | Required for acceptance | Cannot release without |
| Should | Expected but negotiable | Release with known limitation |
| Could | Nice to have | Defer to future iteration |
scenarios:
happy_path:
- id: "AC-01"
title: "Valid form submission"
given: "User on registration form"
when: "User enters valid email, password meeting requirements, and submits"
then: "Account created, confirmation email sent, user redirected to dashboard"
error_handling:
- id: "AC-02"
title: "Invalid email format"
given: "User on registration form"
when: "User enters 'notanemail' in email field and attempts submit"
then: "Inline error appears: 'Please enter a valid email address', form not submitted"
- id: "AC-03"
title: "Password too weak"
given: "User on registration form"
when: "User enters password shorter than 8 characters"
then: "Inline error appears with password requirements, form not submitted"
scenarios:
happy_path:
- id: "AC-01"
title: "Search returns results"
given: "User on product listing with 100 products"
when: "User searches for 'laptop'"
then: "Results filtered to products containing 'laptop', result count displayed"
edge_cases:
- id: "AC-02"
title: "No search results"
given: "User on product listing"
when: "User searches for 'xyznonexistent'"
then: "Empty state displayed: 'No products found', suggestion to modify search"
- id: "AC-03"
title: "Empty search"
given: "User on product listing with active search filter"
when: "User clears search field"
then: "All products displayed, filter cleared"
| Anti-Pattern | Example | Fix |
|---|---|---|
| Vague outcome | "Then it works correctly" | Specific observable behavior |
| Implementation detail | "Then database is updated" | User-visible outcome |
| Compound actions | "When user fills form and submits and sees result" | Split into steps |
| Missing error paths | Only happy path | Add error handling scenarios |
| Untestable | "Then user is satisfied" | Measurable outcome |
Before finalizing: