Step Definitions
Step definitions are Python functions that execute Gherkin steps. Good steps are reusable, thin wrappers over application code, and share state through fixtures or context objects.
Search across all documentation pages
Step definitions are Python functions that execute Gherkin steps. Good steps are reusable, thin wrappers over application code, and share state through fixtures or context objects.
from pytest_bdd import given, when, then, parsers
@given(parsers.parse('a user with email "{email}"'))
def user_with_email(email, user_factory):
return user_factory(email=email)
@when("they log in")
def login(user_with_email, auth_client):
auth_client.login(user_with_email)
@then(parsers.parse('they see "{message}"'))
def see_message(message, page):
assert message in page.contentWhen to reach for this:
# features/steps/common.py
from pytest_bdd import given, when, then, parsers
import httpx
class APIContext:
def __init__(self):
self.client = None
self.response = None
self.token = None
@given("an authenticated API client")
def auth_client(context: APIContext, test_token):
context.client = httpx.Client(
base_url="http://testserver",
headers={"Authorization": f"Bearer {test_token}"},
)
context.token = test_token
@when(parsers.parse('I send a {method} request to "{path}"'))
def send_request(context: APIContext, method, path):
context.response = context.client.request(method, path)
@then(parsers.parse("the response status is {code:d}"))
def check_status(context: APIContext, code):
assert context.response.status_code == code
@then(parsers.cfparse("the response contains {count:d} items"))
def check_count(context: APIContext, count):
data = context.response.json()
assert len(data) == countWhat this demonstrates:
parsers.parse and parsers.cfparse extract typed parametersparsers.parse for {name}, {name:d}, {name:f}, {name:w} (word)Feature file (what)
-> Step definition (thin glue)
-> Application service (how)
-> Domain logic
"100" vs 100. Fix: use {amount:f} or {amount:d} typed parsers.| Alternative | Use When | Don't Use When |
|---|---|---|
| Generic steps | Highly parametrized domains | Steps become unreadable |
| Page Object Model | UI testing | API-only projects |
| Direct pytest | No Gherkin needed | Stakeholder collaboration required |
Context object, pytest fixtures, or behave's context attribute.
Avoid. Extract shared logic into helper functions instead.
pytest-bdd: target_fixture and datatable parameter. behave: context.table.
Parse format with more type specifiers than basic parse. Part of pytest-bdd.
Publish a shared steps/ package pip-installed as a dev dependency.
Call the Python function directly in unit tests with mock context.
pytest-bdd supports target_fixture=True on @given to inject as fixture.
pytest-bdd with pytest-asyncio: async def step functions.
Gherkin text must match exactly. Check for trailing spaces and quote style.
10-20 common steps (auth, API client, assertions). Feature-specific steps stay local.
Stack versions: This page was written for Python 3.14.0, FastAPI 0.115+, Django 5.2, Flask 3.1, Pydantic 2, PyTorch 2.6+, pandas 2.2+, Polars 1.x, ruff 0.9+, and uv 0.6+.
Reviewed by Chris St. John·Last updated Jul 19, 2026