From qa-bdd
Configures Behave for Python BDD scenarios - `pip install behave`, authors `.feature` files in Gherkin, writes step implementations in `features/steps/*.py`, configures via `environment.py` for setup/teardown hooks, organizes via tags, runs via `behave`. Use for Python codebases that want Cucumber-family BDD without Cucumber-Ruby / Cucumber-JS.
How this skill is triggered — by the user, by Claude, or both
Slash command
/qa-bdd:behave-testingThe summary Claude sees in its skill listing — used to decide when to auto-load this skill
Per [behave-docs][bd]:
Per behave-docs:
"behave is behaviour-driven development, Python style." It employs "tests written in a natural language style, backed up by Python code."
Per behave-docs: "Behavior-driven development encourages collaboration between developers, QA and non-technical or business participants in a software project."
Per cucumber-install,
Behave is in the semi-official Cucumber tier (uses Cucumber
components but maintained outside the main org).
acceptance-criteria-extractor).pip install behave
Behave's conventional layout:
project/
├── features/
│ ├── cart.feature # Gherkin features
│ ├── steps/
│ │ ├── cart_steps.py # step implementations
│ │ └── shared_steps.py
│ └── environment.py # setup/teardown hooks
└── ...
# features/cart.feature
Feature: Apply promo code at checkout
Background:
Given a logged-in user
And the cart contains 1 of "BOOK-001" at $24.99
Scenario: Apply valid promo
When I enter "WELCOME10" in the promo input
And I click "Apply"
Then the subtotal updates to $22.49
Scenario Outline: Promo validation
When I enter "<code>" in the promo input
And I click "Apply"
Then an error appears: "<error>"
Examples:
| code | error |
| EXPIRED50 | This code has expired |
| NOTREAL | Code not found |
# features/steps/cart_steps.py
from behave import given, when, then
from app.cart import Cart
from app.checkout import CheckoutPage
from tests.fixtures import login_user
@given('a logged-in user')
def step_logged_in_user(context):
context.user = login_user()
context.page = CheckoutPage(context.user)
@given('the cart contains {qty:d} of "{sku}" at ${price:f}')
def step_cart_contains(context, qty, sku, price):
context.cart = Cart()
context.cart.add_item(sku, qty, price)
context.page.set_cart(context.cart)
@when('I enter "{code}" in the promo input')
def step_enter_promo(context, code):
context.page.enter_promo(code)
@when('I click "{label}"')
def step_click(context, label):
context.page.click(label)
@then('the subtotal updates to ${expected:f}')
def step_subtotal(context, expected):
assert abs(context.page.get_subtotal() - expected) < 0.01, \
f"Expected {expected}, got {context.page.get_subtotal()}"
@then('an error appears: "{message}"')
def step_error(context, message):
assert message in context.page.get_error_message()
The context object carries state across steps within a scenario.
environment.pyPer behave-docs, environment.py provides "Environmental
Controls":
# features/environment.py
def before_all(context):
"""Once before any scenario runs."""
context.config.setup_logging()
context.db = setup_test_database()
def after_all(context):
"""Once after all scenarios finish."""
context.db.close()
def before_scenario(context, scenario):
"""Before each scenario."""
context.db.start_transaction()
def after_scenario(context, scenario):
"""After each scenario (use scenario.status to check pass/fail)."""
context.db.rollback()
def before_tag(context, tag):
"""Before scenarios with a specific tag."""
if tag == 'requires_browser':
context.browser = launch_browser()
def after_tag(context, tag):
if tag == 'requires_browser':
context.browser.quit()
The hook hierarchy: before_all > before_feature >
before_scenario > before_step (and the matching after_*).
Per behave-docs, "Controlling Things With Tags" is the filter mechanism:
@critical @regression
Scenario: Apply valid promo
...
@wip
Scenario: New checkout flow (work in progress)
...
# Run only critical
behave --tags=critical
# Skip wip
behave --tags=~wip
# Combine
behave --tags=critical --tags=~slow
# Plain text + JUnit XML for CI
behave --junit --junit-directory reports/junit/
# Per-feature output, no colors
behave --format=plain --no-color > test-results.txt
The JUnit XML feeds junit-xml-analysis.
behave # all features
behave features/cart.feature # one feature
behave --tags=@critical # by tag
behave -i cart # match file pattern
Per behave-docs, Behave is the canonical Python BDD; the ecosystem also includes pytest-bdd which integrates Gherkin into pytest. Choose:
| Behave | pytest-bdd |
|---|---|
| Standalone runner | pytest plugin |
| Closer to Cucumber semantics | Reuses pytest fixtures |
| Better for pure-BDD teams | Better when mixing BDD + xUnit-style tests |
| Anti-pattern | Why it fails | Fix |
|---|---|---|
Mixing fixtures across context and module-level state | Hidden coupling; test order matters. | Use context only; reset per scenario via before_scenario (Step 5). |
| Step regex too greedy | One step matches multiple Gherkin lines. | Use {var} placeholders + type hints ({qty:d} per Step 4). |
before_all setup that fails | All scenarios fail; debugging hard. | Quick smoke check in before_all; fail fast with clear message. |
@wip scenarios shipping in CI | Test runner counts them as passes. | behave --tags=~wip in CI (Step 6). |
| One step file with 200 steps | Hard to navigate; merge conflicts. | Split per-feature: cart_steps.py, checkout_steps.py, etc. |
behave-parallel plugin OR
shard at CI level via tag filtering.cucumber-testing,
reqnroll-testing - sibling
language wrappers.bdd-step-library-curator - keeps step proliferation in check.gherkin-style-reviewer - adversarial Gherkin reviewer.npx claudepluginhub testland/qa --plugin qa-bddProvides a checklist for code reviews covering functionality, security, performance, maintainability, tests, and quality. Use for pull requests, audits, team standards, and developer training.