Given/When/Then Template for Specs

Given/When/Then is a practical grammar for behavior specifications. It reduces debate in reviews because each statement clearly identifies setup, trigger, and expected result.

Published November 28, 2025 · Updated March 25, 2026 · Author: Daniel Marsh · Editorial Policy

Quick answer

Use Given to set up state and permissions, When for one trigger action, Then for verifiable outcomes. Keep scenarios independent so they can be tested in isolation and reused across automation suites.

Template rules

Reusable scenario set

# Scenario: Update email (happy path)
- Given an authenticated user with edit permission
- When user updates email to a unique valid value
- Then return 200 and persist new email

# Scenario: Duplicate email
- Given an authenticated user
- When user updates email to an existing value
- Then return 409 with standardized conflict error

# Scenario: Invalid format
- Given an authenticated user
- When user updates email with invalid format
- Then return 400 validation error and keep old value

# Scenario: Permission denied
- Given a read-only role user
- When user attempts to update email
- Then return 403 and no data mutation occurs

Common writing mistakes

If a scenario can't become a test without clarifying questions, rewrite it before implementation starts.

How to use in spec reviews

Extended scenario patterns

The basic email-update example covers single-actor, synchronous interactions. Real systems also need patterns for asynchronous outcomes, multi-actor flows, and background job behavior. The structure stays the same — Given sets state, When triggers action, Then asserts observable outcome — but the scope of "observable" expands.

# Scenario: Async notification after order completion
- Given an order with status = pending_fulfillment
- When fulfillment service marks the order as shipped
- Then a notification event is published to the customer.notifications topic
  And notification_log records entry with orderId and timestamp

# Scenario: Multi-actor: admin overrides user lock
- Given a user account locked after 5 failed login attempts
- When an admin submits an account unlock with valid reason
- Then user.status changes to active
  And audit log records admin ID, reason, and timestamp

# Scenario: Background job idempotency
- Given a scheduled invoice job runs with jobId = job-999
- When the job is triggered a second time with the same jobId
- Then no duplicate invoices are created
  And second run returns early with logged skip event

Async scenarios should specify the observable artifact — a published event, a database record, a log entry — rather than the internal mechanism that produced it. If the Then clause requires knowledge of implementation internals to verify, rewrite it to a system-level observable.

When to split or merge scenarios

One of the most common structural mistakes is writing scenarios that cover multiple behavior branches. The rule is: one behavior branch per scenario. A scenario that tests both the happy path and the validation error in a single Given/When/Then block cannot be converted to a single test case without cheating — the test either misses a path or makes hidden assumptions.

Split a scenario when the Then clause contains two distinct verifiable assertions that could independently fail, or when the scenario title would require the word "and" to describe what it tests. Merge two scenarios when they share identical Given state and differ only in trivially equivalent inputs — for example, two equivalent valid formats that produce the same response. Keep scenarios separate when permission paths diverge; reader vs editor vs admin roles almost always need independent scenarios.

A quick rule for review: if a scenario cannot be phrased as a single declarative test name in your test framework, it is too broad. Rename it first — if the name requires a conjunction, split the scenario.

Mapping scenarios to automated tests

A scenario is only complete when it is linked to a test. The link does not have to be a test ID — it can be a file path, a test function name, or a Gherkin feature file location. What matters is that the traceability exists before implementation begins, not after.

If the harness cannot express the Given state — because the fixture system does not support a required precondition — that is a signal to extend the harness, not to weaken the scenario. A scenario that is simplified to fit the test framework becomes unreliable specification.

Use these templates

Given/When/Then is most effective when paired with explicit non-goals and edge-case checklists in the same spec.

Editorial note

This guide covers the Given/When/Then Template for spec-first engineering teams. Examples are illustrative scenarios, not production code.