In Agile, the user story replaces the traditional requirements document as the primary input for test design. But a user story alone โ “As a user, I want to reset my password” โ is too vague to test against. The acceptance criteria attached to the story define the specific, testable conditions that must be true for the story to be considered complete. And the Definition of Done (DoD) defines the team-wide standard that every story must meet beyond its individual acceptance criteria. Testers play a critical role in shaping all three.
From User Story to Testable Acceptance Criteria
A well-written user story with strong acceptance criteria gives the tester everything they need to design test cases without ambiguity. A poorly written story forces the tester to guess โ and guessing leads to missed coverage and wasted time.
# User story with acceptance criteria and derived test cases
user_story = {
"id": "US-087",
"story": (
"As a registered user, I want to reset my password via email "
"so that I can regain access to my account if I forget my password."
),
"acceptance_criteria": [
{
"ac_id": "AC-1",
"given": "I am on the login page",
"when": "I click 'Forgot Password' and enter my registered email",
"then": "A password reset email is sent within 60 seconds",
},
{
"ac_id": "AC-2",
"given": "I have received a reset email",
"when": "I click the reset link within 30 minutes",
"then": "I am taken to the 'Set New Password' page",
},
{
"ac_id": "AC-3",
"given": "I am on the 'Set New Password' page",
"when": "I enter a valid password (8+ chars, 1 uppercase, 1 digit, 1 special)",
"then": "My password is updated and I see a success confirmation",
},
{
"ac_id": "AC-4",
"given": "I have received a reset email",
"when": "I click the reset link after 30 minutes",
"then": "I see an error message: 'This link has expired. Please request a new one.'",
},
{
"ac_id": "AC-5",
"given": "I am on the Forgot Password page",
"when": "I enter an email that is not registered",
"then": (
"I see the same confirmation message as AC-1 (to prevent email enumeration) "
"but no email is actually sent"
),
},
],
}
# Derive test cases from acceptance criteria
print(f"User Story: {user_story['id']}")
print(f" {user_story['story']}\n")
print("Acceptance Criteria โ Test Cases:")
print("=" * 60)
for ac in user_story['acceptance_criteria']:
print(f"\n {ac['ac_id']}:")
print(f" GIVEN: {ac['given']}")
print(f" WHEN: {ac['when']}")
print(f" THEN: {ac['then']}")
# Definition of Done โ team-wide standard
DEFINITION_OF_DONE = [
"All acceptance criteria are met and verified by QA",
"Code is peer-reviewed and merged to main branch",
"Unit tests pass with >= 80% coverage on new code",
"No open P1 or P2 defects for this story",
"Regression suite passes (automated)",
"Documentation updated (API docs, user guides if applicable)",
"Product Owner has accepted the story in sprint review",
]
print("\n\nDefinition of Done (team-wide):")
print("=" * 60)
for item in DEFINITION_OF_DONE:
print(f" [ ] {item}")
Common Mistakes
Mistake 1 โ Accepting user stories without acceptance criteria
โ Wrong: “The story says ‘As a user, I want to reset my password.’ That is clear enough โ I will figure out the test cases myself.”
โ Correct: “Before this story enters a sprint, it needs acceptance criteria covering the happy path, error conditions (expired link, unregistered email), and security considerations (preventing email enumeration). I will draft these during refinement.”
Mistake 2 โ Treating the Definition of Done as optional or aspirational
โ Wrong: “The DoD says regression tests must pass, but we are behind schedule so we will skip regression this sprint.”
โ Correct: “The DoD is a non-negotiable team commitment. If regression tests cannot pass because of known defects, the story is not done. We either fix the defects or remove the story from the sprint so our delivered work genuinely meets the quality bar.”