Lesson 11: Gherkin Language
On this page
Learning Objectives
By the end of this lesson, you will be able to:
- Write well-structured feature files using correct Gherkin syntax including features, scenarios, and step definitions
- Apply Gherkin keywords effectively (Given, When, Then, And, But, Background) to create clear, readable specifications
- Use advanced Gherkin features including data tables, scenario outlines, and tags to reduce duplication and improve maintainability
- Organize feature files using best practices for structure, naming, and modularity that scale with project complexity
- Identify and fix common Gherkin anti-patterns that reduce scenario clarity and maintainability
Introduction
Gherkin is the language used to write BDD scenarios—a business-readable, domain-specific language that lets you describe software behavior without detailing how that behavior is implemented. Created as part of the Cucumber project, Gherkin has become the standard language for BDD across many frameworks and programming languages [1].
The genius of Gherkin lies in its simplicity. With just a handful of keywords (Feature, Scenario, Given, When, Then), you can describe complex behavior in a way that both business stakeholders and technical team members understand. This shared language eliminates the ambiguity that plagues traditional requirements documents while creating executable specifications that verify behavior automatically [2].
Gherkin’s power extends beyond basic scenarios. Data tables enable testing with multiple data sets without duplication. Scenario outlines create parameterized tests from examples. Tags organize and filter scenarios for different execution contexts. These features transform Gherkin from a simple specification language into a comprehensive tool for behavior documentation and test automation [3].
However, Gherkin’s simplicity can be deceptive. Writing clear, maintainable Gherkin requires discipline and understanding of best practices. Poorly written scenarios become brittle, hard to understand, and expensive to maintain. This lesson teaches you not just Gherkin syntax but the principles behind writing effective specifications that remain valuable throughout a project’s lifetime [4].
Core Content
Gherkin Syntax Fundamentals
Gherkin uses a structure of keywords followed by text. The text after keywords is not parsed by Gherkin—it’s free-form text for human readers [1].
Basic Structure:
# Comments start with hash
Feature: Brief description of the feature
More detailed description can go here
over multiple lines
Background:
Steps that run before each scenario
Scenario: Description of this specific scenario
Given some initial context
When an action occurs
Then verify the outcomeCore Keywords:
- Feature: Container for related scenarios, describes high-level functionality
- Scenario: Single test case describing specific behavior
- Given: Preconditions or initial state
- When: Action or event that triggers behavior
- Then: Expected outcome or result
- And/But: Additional steps of the same type (readability)
- Background: Steps that run before every scenario in the feature
Feature Block:
Feature: User Authentication
As a registered user
I want to log in to my account
So that I can access personalized features
This feature covers:
- Login with email and password
- Account lockout after failed attempts
- Password reset functionalityThe feature description provides context but isn’t executed. Use it to explain the feature’s purpose and scope.
Scenario Structure:
Scenario: Successful login with valid credentials
Given I am a registered user with email "[email protected]"
And my password is "SecurePass123"
When I enter my credentials on the login page
And I click the "Log In" button
Then I should see the dashboard
And I should see a welcome message
But I should not see the login formStep Keywords:
All step keywords (Given, When, Then, And, But) are functionally equivalent to Gherkin—the distinction is for human readability:
# These work identically
Given user is logged in
And cart has items
And payment method is set
# Could be written as:
Given user is logged in
Given cart has items
Given payment method is set
# But "And" reads more naturallyBackground: Shared Setup
Background runs before each scenario in a feature, reducing duplication [1].
Feature: Shopping Cart Management
Background:
Given I am logged in as "[email protected]"
And I am on the products page
Scenario: Add single item to cart
When I click "Add to Cart" for "Laptop"
Then my cart should contain 1 item
And the item should be "Laptop"
Scenario: Add multiple items to cart
When I add "Laptop" to my cart
And I add "Mouse" to my cart
Then my cart should contain 2 items
Scenario: Remove item from cart
Given I have "Laptop" in my cart
When I remove "Laptop" from my cart
Then my cart should be emptyEach scenario runs with the Background steps first:
- Log in
- Navigate to products page
- Execute scenario steps
When to Use Background:
- Good: Setup common to all scenarios (login, navigation, test data)
- Avoid: Background steps that only apply to some scenarios
- Avoid: Complex backgrounds that make scenarios hard to understand
# Bad - overly complex background
Background:
Given the system has the following users:
| email | role | status |
| [email protected] | admin | active |
| [email protected] | user | active |
And the system has the following products:
| name | price | stock |
| Laptop | 999 | 50 |
| Mouse | 29 | 100 |
And the payment gateway is configured
And email service is running
# Too much! Most scenarios don't need all this
# Good - minimal background
Background:
Given I am logged in as a standard userData Tables: Structured Data
Data tables pass lists of data to step definitions [1].
Simple Lists:
Scenario: Add multiple items to cart
Given I have the following items:
| Laptop |
| Mouse |
| Keyboard |
When I calculate the total
Then I should see 3 items in my cartTables with Headers:
Scenario: Calculate order total with multiple items
Given I have the following items in my cart:
| product | price | quantity |
| Laptop | 999 | 1 |
| Mouse | 29 | 2 |
| Keyboard | 79 | 1 |
When I proceed to checkout
Then my subtotal should be $1,136Vertical Tables (Property Lists):
Scenario: Create user account
Given I register with the following details:
| field | value |
| email | [email protected] |
| password | SecurePass123 |
| firstName | John |
| lastName | Doe |
When my account is created
Then I should receive a welcome emailStep Implementation Example:
@given('I have the following items in my cart')
def step_impl(context):
context.cart = ShoppingCart()
for row in context.table:
product = row['product']
price = Decimal(row['price'])
quantity = int(row['quantity'])
context.cart.add_item(product, price, quantity)Scenario Outline: Parameterized Tests
Scenario outlines run the same scenario multiple times with different data [1].
Basic Scenario Outline:
Scenario Outline: Login with different credentials
Given I am on the login page
When I enter email "<email>"
And I enter password "<password>"
Then I should see "<outcome>"
Examples:
| email | password | outcome |
| [email protected] | CorrectPass1 | Dashboard |
| [email protected] | WrongPass | Invalid email or password |
| [email protected] | WrongPass | Invalid email or password |
| [email protected] | CorrectPass1 | Account locked |This creates 4 separate scenarios, one for each row in Examples.
Multiple Example Tables:
Scenario Outline: Apply discount based on customer tier
Given I am a "<tier>" customer
And my cart total is $<amount>
When I proceed to checkout
Then my discount should be $<discount>
Examples: Standard customers
| tier | amount | discount |
| bronze | 100 | 5 |
| silver | 100 | 10 |
| gold | 100 | 20 |
Examples: Premium customers
| tier | amount | discount |
| platinum | 100 | 25 |
| diamond | 100 | 30 |When to Use Scenario Outlines:
# Good - same behavior, different data
Scenario Outline: Validate email format
When I enter email "<email>"
Then validation should "<result>"
Examples:
| email | result |
| [email protected] | pass |
| invalid | fail |
| user@ | fail |
| @example.com | fail |
# Bad - different behaviors, forced into one outline
Scenario Outline: User actions
When I perform "<action>"
Then I see "<result>"
Examples:
| action | result |
| login | dashboard |
| view_profile | profile_page |
| logout | login_page |
# These are different behaviors, should be separate scenarios!Tags: Organization and Filtering
Tags enable organizing and selectively executing scenarios [3].
Basic Tags:
@authentication @critical
Feature: User Login
@smoke @fast
Scenario: Successful login
Given valid user credentials
When I log in
Then I should see the dashboard
@security @slow
Scenario: Account lockout after failed attempts
Given I have attempted login 5 times with wrong password
When I attempt to login again
Then I should see "Account locked"Running Tagged Scenarios:
# Run scenarios with specific tag
behave --tags=@smoke
# Run scenarios with any of multiple tags (OR)
behave --tags=@critical --tags=@security
# Run scenarios with all tags (AND)
behave --tags=@authentication,@critical
# Exclude scenarios with tag
behave --tags=~@slow
# Complex expressions
behave --tags=@critical --tags=~@wip
# Runs critical scenarios that are not work-in-progressCommon Tag Conventions:
# Execution speed
@fast # Quick tests (< 1 second)
@slow # Longer tests (> 5 seconds)
# Test level
@unit # Unit-level behavior
@integration # Integration tests
@e2e # End-to-end tests
# Priority
@critical # Must always pass
@important # High priority
@nice_to_have # Low priority
# Status
@wip # Work in progress (skip in CI)
@manual # Manual test (not automated)
@automated # Fully automated
@flaky # Known to be unreliable (needs fixing)
# Functional area
@authentication
@shopping_cart
@payment
@reporting
# Environment
@requires_staging # Only runs in staging
@production_safe # Safe to run against productionFeature-Level Tags:
@shopping @critical
Feature: Shopping Cart
@smoke
Scenario: Add item to empty cart
# Inherits @shopping and @critical from feature
# Also has @smokeScenario Organization Best Practices
One Feature, One File:
features/
├── authentication/
│ ├── login.feature
│ ├── registration.feature
│ ├── password_reset.feature
│ └── two_factor_auth.feature
├── shopping/
│ ├── cart.feature
│ ├── checkout.feature
│ ├── payment.feature
│ └── discounts.feature
└── account/
├── profile.feature
├── order_history.feature
└── preferences.featureFeature File Structure:
# Descriptive file name: user_registration.feature
@registration @authentication
Feature: User Registration
As a new visitor
I want to create an account
So that I can access personalized features
# Rules document business logic
Rules:
- Email must be unique
- Password must be at least 8 characters
- Account requires email verification
Background:
Given I am on the registration page
# Happy path first
@smoke @critical
Scenario: Successful registration with valid data
When I register with valid details
Then I should see "Registration successful"
And I should receive a verification email
# Then alternatives and edge cases
Scenario: Registration fails with duplicate email
Given a user exists with email "[email protected]"
When I attempt to register with email "[email protected]"
Then I should see "Email already registered"
Scenario: Registration fails with weak password
When I attempt to register with password "weak"
Then I should see "Password must be at least 8 characters"
@security
Scenario: Registration requires email verification
Given I have registered but not verified my email
When I attempt to log in
Then I should see "Please verify your email first"Writing Clear Step Text
Use Domain Language:
# Bad - technical implementation
Given the UserRepository contains a record with id=123
When the AuthService.authenticate() is invoked
Then the JWT token should be generated
# Good - business language
Given I am a registered user
When I log in with valid credentials
Then I should have access to my accountBe Specific:
# Bad - vague
Given some products exist
When I search for something
Then I should see results
# Good - concrete
Given the following products exist:
| name | category | price |
| Laptop | Electronics | 999 |
| Mouse | Electronics | 29 |
When I search for "Laptop"
Then I should see 1 search result
And the result should be "Laptop"Avoid Implementation Details:
# Bad - specifies HOW
Given I navigate to URL "/login"
And I fill in the field with id "email" with "[email protected]"
And I fill in the field with id "password" with "pass123"
And I click the button with class "btn-submit"
# Good - specifies WHAT
Given I am on the login page
When I log in as "[email protected]" with password "pass123"Keep Steps Atomic:
# Bad - compound step
When I add a laptop to cart and proceed to checkout and enter payment details
# Good - separate steps
When I add a laptop to cart
And I proceed to checkout
And I enter payment detailsCommon Pitfalls
Pitfall 1: Incidental Details
Including unnecessary details makes scenarios brittle and hard to read.
# Bad - too many incidental details
Scenario: User updates profile
Given I am on the profile page
And the "First Name" field contains "John"
And the "Last Name" field contains "Doe"
And the "Phone" field contains "555-1234"
And the "Address" field contains "123 Main St"
And the "City" field contains "Springfield"
When I change "Last Name" to "Smith"
And I click "Save"
Then I should see "Profile updated"
And the "Last Name" field should contain "Smith"
# Good - focused on what matters
Scenario: User updates profile
Given I have a profile with last name "Doe"
When I change my last name to "Smith"
Then my profile should show last name "Smith"Best Practice: Include only details relevant to the scenario’s purpose [2].
Pitfall 2: UI-Coupled Steps
Steps that reference UI elements break when UI changes.
# Bad - coupled to UI
When I click the button with id "submit-btn"
Then I should see text in div class "success-message"
# Good - behavior-focused
When I submit the form
Then I should see a success messageBest Practice: Describe user intentions and outcomes, not UI mechanics [4].
Pitfall 3: Scenario Interdependence
Scenarios that depend on each other create fragile test suites.
# Bad - scenarios depend on each other
Scenario: Create user account
When I register as "[email protected]"
Then my account should be created
Scenario: Login with new account
# Depends on previous scenario!
When I log in as "[email protected]"
Then I should see the dashboardBest Practice: Each scenario must be independently executable. Use Background or explicit Given steps to set up needed state [3].
Pitfall 4: Testing Multiple Behaviors
One scenario testing multiple unrelated behaviors makes failures ambiguous.
# Bad - multiple behaviors
Scenario: User account management
Given I am logged in
When I update my profile
Then my profile should be updated
When I change my password
Then my password should be changed
When I enable two-factor auth
Then two-factor auth should be enabled
# Good - separate scenarios
Scenario: Update user profile
Given I am logged in
When I update my profile
Then my profile should be updated
Scenario: Change password
Given I am logged in
When I change my password
Then my password should be changed
Scenario: Enable two-factor authentication
Given I am logged in
When I enable two-factor auth
Then two-factor auth should be enabledBest Practice: One scenario, one behavior [2].
Summary
Gherkin is a business-readable language for writing BDD scenarios that serve as both specifications and automated tests. Its simple syntax—features, scenarios, and Given-When-Then steps—enables clear communication between technical and non-technical stakeholders.
Basic syntax uses keywords (Feature, Scenario, Given, When, Then) followed by free-form text. Given establishes context, When describes actions, Then verifies outcomes. And/But improve readability for multiple steps of the same type.
Background provides shared setup for all scenarios in a feature, reducing duplication. Use backgrounds for common preconditions, but keep them simple—complex backgrounds make scenarios hard to understand.
Data tables pass structured data to steps, enabling tests with multiple data points. Use tables with headers for clarity, and consider vertical tables (property lists) for single entities with many attributes.
Scenario outlines create parameterized tests by running the same scenario with different data from Examples tables. Use outlines when testing the same behavior with multiple inputs, not for forcing different behaviors into one template.
Tags organize scenarios for selective execution. Use tags for execution speed (@fast, @slow), test level (@unit, @integration), priority (@critical), status (@wip), and functional areas (@authentication). Run specific tags to focus test execution.
Organization best practices include one feature per file, clear directory structure by functional area, rules to document business logic, and presenting happy path scenarios before edge cases. Good organization makes feature files navigable and maintainable.
Clear step text uses domain language, specifies concrete examples, avoids implementation details, and keeps steps atomic. Steps should describe what users do and see, not how the system implements it.
Common pitfalls include incidental details (include only relevant information), UI-coupled steps (describe behavior, not UI mechanics), scenario interdependence (each scenario must be independent), and testing multiple behaviors (one scenario, one behavior).
Well-written Gherkin creates specifications that remain valuable throughout a project’s lifetime—understandable by all stakeholders, executable as tests, and serving as accurate documentation that evolves with the system.
Practice Quiz
Question 1: Your team writes this Gherkin scenario. What’s wrong with it, and how would you improve it?
Scenario: Checkout
Given I am on page "/products"
When I click element with id "add-to-cart-btn"
And I click link with text "Cart"
And I fill in textbox id "name" with "John Doe"
And I fill in textbox id "card" with "4111111111111111"
And I select "USA" from dropdown id "country"
And I click button class "submit-payment"
Then I should see text in div class "order-confirmation"Answer: Multiple problems:
Issues:
- UI-coupled steps - references HTML elements (id, class)
- Implementation details - specifies HOW (click element, fill textbox)
- Incidental details - includes specific values not relevant to behavior
- Generic title - “Checkout” doesn’t describe specific behavior
- No clear behavior - unclear what’s being tested
Fixed version:
Scenario: Customer completes purchase with credit card
Given I have items in my shopping cart
When I proceed to checkout
And I enter my shipping information
And I pay with a valid credit card
Then I should see order confirmation
And I should receive a confirmation emailFurther improvements with data tables:
@payment @critical
Scenario: Complete purchase with credit card
Given I have the following items in my cart:
| product | price |
| Laptop | 999 |
When I checkout with the following details:
| field | value |
| name | John Doe |
| cardNumber | 4111111111111111 |
| shippingCountry| USA |
Then I should see "Order confirmed"
And my order total should be $999
And I should receive an order confirmation emailThis version focuses on behavior (what the user accomplishes), uses domain language (checkout, pay, order confirmation), and avoids implementation details (HTML elements, button clicks) [2][4].
Question 2: When should you use a Scenario Outline instead of multiple separate scenarios?
Answer: Use Scenario Outlines when testing the same behavior with different data. DON’T use them to force different behaviors into one template.
Good use - same behavior, different data:
Scenario Outline: Validate email format
When I enter email "<email>"
Then validation should "<result>"
And I should see "<message>"
Examples:
| email | result | message |
| [email protected] | pass | Valid email |
| invalid | fail | Must include @ symbol |
| user@ | fail | Must include domain |
| @example.com | fail | Must include username |
| user@example | fail | Must include domain suffix |All examples test the same behavior (email validation) with different inputs.
Bad use - different behaviors forced together:
# DON'T DO THIS
Scenario Outline: User operations
When I "<action>"
Then I see "<result>"
Examples:
| action | result |
| log in | dashboard |
| view profile | profile page |
| update settings | settings saved |
| log out | login page |These are different behaviors (login, view, update, logout), not the same behavior with different data. They should be separate scenarios.
Rule of thumb:
- Use Scenario Outline: Testing validation rules, calculations with different values, same user journey with different data
- Use separate scenarios: Different user actions, different features, different behaviors [1][3]
Question 3: Your feature file has this Background section. What problems might this cause?
Background:
Given the following users exist:
| email | role | tier |
| [email protected] | admin | platinum |
| [email protected] | user | gold |
| [email protected] | user | bronze |
And the following products exist:
| name | category | price | stock |
| Laptop | Electronics | 999 | 10 |
| Mouse | Electronics | 29 | 50 |
| Keyboard | Electronics | 79 | 30 |
And the payment gateway is configured
And the email service is running
And the inventory service is runningAnswer: This background is too complex and creates unnecessary dependencies.
Problems:
- Unnecessary setup - Most scenarios don’t need all this data
- Slow execution - Creates extensive test data before every scenario
- Unclear dependencies - Hard to tell what each scenario actually needs
- Difficult to debug - When something fails, is it the background or scenario?
- Tight coupling - All scenarios must work with this exact data
Fixed approach:
Option 1: Minimal background
Background:
Given I am logged in as a standard user
# Only what EVERY scenario truly needsOption 2: Scenario-specific setup
# No background - each scenario sets up what it needs
Scenario: Add item to cart
Given I am logged in
And a product "Laptop" exists with price $999
When I add "Laptop" to my cart
Then my cart should contain 1 item
Scenario: Apply discount code
Given I am a gold tier customer
And I have items worth $100 in my cart
When I apply discount code "SAVE10"
Then my discount should be $10Option 3: Background with common context only
Background:
Given I am on the products page
And I am logged in
# Each scenario adds its specific needs
Scenario: Search for product
Given products exist in multiple categories
When I search for "Laptop"
...Guideline: Background should only include setup that every single scenario in the feature needs. If only some scenarios need it, put it in those scenarios [1][2].
Question 4: You’re writing Gherkin for a complex discount calculation system. Should you include the calculation formula in the scenario?
Answer: Yes, if the calculation IS the behavior you’re specifying. The key is focusing on behavior, not implementation.
Good - specifies business rule:
@calculation @critical
Scenario: Calculate volume discount for large orders
Given the unit price is $100
And the volume discount tiers are:
| quantity | discount |
| 10-49 | 5% |
| 50-99 | 10% |
| 100+ | 15% |
When I order 75 units
Then the subtotal should be $7,500 # 75 × $100
And the discount should be $750 # 10% of $7,500
And the total should be $6,750 # $7,500 - $750This is good because:
- It documents the business rule (volume discount tiers)
- It shows concrete examples with calculations
- It verifies expected behavior with specific numbers
- Business stakeholders can verify the logic
Bad - specifies technical implementation:
# DON'T DO THIS
Scenario: Calculate discount
Given the DiscountCalculator class is instantiated
When the calculateDiscount() method is called with order
Then the method should iterate through discount_tiers array
And apply the matching tier's percentage using multiply operator
And return a DiscountResult objectThis is bad because:
- It describes HOW the code works internally
- It’s coupled to implementation (class names, method names)
- It breaks when you refactor
- Non-technical stakeholders can’t understand it
Rule: Include calculations/logic when they represent business rules. Avoid describing technical implementation [2][4].
Question 5: Your project has 500+ scenarios. How should you organize them with tags to make them manageable?
Answer: Use a multi-dimensional tagging strategy covering different concerns:
@authentication @api @fast @critical
Feature: API Authentication
@smoke @happy_path
Scenario: Successful API authentication with valid token
# Tags: authentication, api, fast, critical, smoke, happy_path
@security @rate_limiting @slow
Scenario: API blocks requests after rate limit exceeded
# Tags: authentication, api, slow, critical, security, rate_limiting
@shopping @ui @integration
Feature: Shopping Cart
@smoke @fast
Scenario: Add item to empty cart
# Tags: shopping, ui, integration, smoke, fast
@payment @slow @requires_test_data
Scenario: Complete checkout with multiple items
# Tags: shopping, ui, integration, payment, slow, requires_test_dataTag dimensions:
1. Functional area (what feature)
@authentication @shopping @payment @user_management @reporting2. Technical layer (where it runs)
@api @ui @database @unit @integration @e2e3. Execution speed (how long)
@fast # < 1 second
@medium # 1-5 seconds
@slow # > 5 seconds4. Priority (how important)
@critical # Must always pass
@important # High priority
@optional # Nice to have5. Test suite (when to run)
@smoke # Quick confidence check (run always)
@regression # Full regression (run before release)
@nightly # Extensive tests (run overnight)6. Status (implementation state)
@wip # Work in progress (skip in CI)
@manual # Not yet automated
@flaky # Known to be unreliable (needs fixing)Running strategies:
# During development (seconds)
behave --tags=@fast --tags=~@wip
# Before commit (minutes)
behave --tags=@critical --tags=@important
# CI smoke tests (2-3 minutes)
behave --tags=@smoke
# Full CI build (10-20 minutes)
behave --tags=@regression --tags=~@slow
# Nightly comprehensive (hours)
behave --tags=@nightly
# Feature-specific (development)
behave --tags=@shopping,@fast
# Only fast shopping scenarios
# Security audit
behave --tags=@security
# API-only tests
behave --tags=@api --tags=~@uiDocumentation:
Create tags.md explaining your tag strategy:
# Tag Conventions
## Functional Areas
- @authentication - Login, registration, password reset
- @shopping - Cart, products, search
- @payment - Checkout, payment processing
## Execution
- @smoke - Quick validation (run always)
- @critical - Must pass before release
- @fast - Under 1 second
## Status
- @wip - Skip in CI
- @flaky - Needs investigationThis multi-dimensional approach enables running exactly the scenarios you need for any context [3].
References
[1] Cucumber Documentation. (2024). Gherkin Reference. URL: https://cucumber.io/courses/gherkin/reference/, Quote: “Gherkin is a Business Readable, Domain Specific Language that lets you describe software’s behavior without detailing how that behavior is implemented. Gherkin uses a set of special keywords (Feature, Scenario, Given, When, Then) to give structure and meaning to executable specifications. The text following these keywords is not parsed by Gherkin—it’s free-form text for human readers.”
[2] Adzic, G. (2011). Specification by Example: How Successful Teams Deliver the Right Software. Manning Publications. URL: https://www.manning.com/books/specification-by-example, Quote: “Good scenarios focus on business rules and behavior, not implementation details. Avoid coupling scenarios to UI elements, technical APIs, or internal architecture. Scenarios should describe what users accomplish and what outcomes they observe, not how the system implements those outcomes. This keeps specifications maintainable when implementation changes.”
[3] Wynne, M., & Hellesoy, A. (2017). The Cucumber Book: Behaviour-Driven Development for Testers and Developers (2nd Edition). Pragmatic Bookshelf. URL: https://pragprog.com/titles/hwcuc2/the-cucumber-book-second-edition/, Quote: “Tags provide a powerful way to organize and filter scenarios. Use tags for functional areas, execution speed, priority, and status. Multi-dimensional tagging enables running exactly the scenarios you need: smoke tests during development, critical tests before commits, comprehensive suites in CI. Each scenario can have multiple tags serving different purposes.”
[4] Smart, J. F. (2014). BDD in Action: Behavior-Driven Development for the Whole Software Lifecycle. Manning Publications. URL: https://www.manning.com/books/bdd-in-action, Quote: “Effective Gherkin scenarios avoid incidental details—information not relevant to the behavior being tested. Include only what matters for understanding and verifying the scenario. Extra details make scenarios brittle (they break when irrelevant aspects change) and hard to read (readers must figure out what’s important). Focus scenarios on their core purpose: specifying one behavior clearly.”