Generates pytest test stubs from Gherkin .feature files, checks consistency, and displays BDD steps — automatically, every time pytest runs.
pytest-beehave is a pytest plugin powered by beehave. It reads your Gherkin .feature files and generates Hypothesis-compatible test stubs with the right names, decorators, and parameters — then verifies that your test code stays consistent with your spec. New scenarios get @pytest.mark.skip; drift becomes a real test failure.
pip install pytest-beehave
No conftest.py changes required. The plugin registers itself via pytest's entry-point system.
Optional: install pytest-beehave[html] for a "Scenario" column in pytest-html reports.
pip install "pytest-beehave[html]"
Quick start
1. Write a feature file
# docs/features/checkout/shopping_cart.feature
Feature: Shopping cart
Background:
Given an empty cart
Rule: Tax calculation
Scenario: VAT is applied at the correct rate
Given a cart with items totalling $100
When the buyer is in the UK
Then the order total is $120
2. Run pytest
pytest --collect-only
3. A test stub is created
tests/features/shopping_cart/
├── tax_calculation_test.py
# tests/features/shopping_cart/tax_calculation_test.py
import pytest
@pytest.mark.skip(reason="not implemented")
def test_VAT_is_applied_at_the_correct_rate():
...
4. See BDD steps
pytest -v
tests/features/shopping_cart/tax_calculation_test.py::test_VAT_is_applied_at_the_correct_rate SKIPPED
Given an empty cart
Given a cart with items totalling $100
When the buyer is in the UK
Then the order total is $120
5. Implement and ship
Remove @pytest.mark.skip, write the test body, run pytest again. The steps display stays in sync with your feature file.
How it works
The plugin hooks into pytest_configure — every stub exists on disk before collection begins.
pytest invoked
└─ pytest_configure fires
├─ load_config() → read [tool.beehave] from pyproject.toml
├─ parse_feature() → parse .feature files into ScenarioInfo
├─ generate_stubs() → write Hypothesis test stubs to disk
├─ _add_skip_markers() → mark unimplemented stubs with @pytest.mark.skip
├─ check_all() → detect drift between features and tests
└─ register display plugins → StepsReporter (-v) and/or HtmlStepsPlugin
└─ pytest_collection_modifyitems → inject failing tests for ERROR violations
└─ Collection begins — every stub is already present
File layout
docs/features/ ← configured via features_dir
**/*.feature ← any subfolder structure is supported
tests/features/ ← configured via tests_dir
<feature_slug>/ ← one directory per feature (derived from Feature title)
<rule_slug>_test.py ← one file per Rule: block (or default_test.py)
Each test function name follows test_<scenario_title_with_underscores>. The mapping is exact string equality — no @id tags, no step definitions, no glue code.
What check_all enforces
After stub generation, the plugin runs check_all() to detect drift between feature files and test code. ERROR violations produce real test failures via synthetic test items:
| Type | Severity | What it catches |
|---|---|---|
unmapped-scenario |
ERROR | Scenario has no matching test function |
unmapped-test |
ERROR | Test function has no matching scenario |
misplaced-test |
WARNING | Function is in the wrong rule file |
missing-placeholder |
ERROR | Test body missing a <placeholder> |
missing-literal |
ERROR | Test body missing a "string" or numeric literal |
example-mismatch |
ERROR | Examples rows don't match @example() decorators |
$ pytest
[beehave] ERROR: tests/features/demo/default_test.py:5: unmapped-test: 'test_orphan' has no matching scenario
========================= 1 failed, 3 passed, 2 skipped =========================
Stub functions (body is ...) are excluded from placeholder and literal checks.
How it maps
- Scenario title → function name:
VAT Is Applied At The Correct Rate→test_VAT_is_applied_at_the_correct_rate. Lowercased. Globally unique. - Rule → test file: Top-level scenarios go to
default_test.py. Scenarios inside a Rule go to<rule>_test.py. - Feature title → directory:
Shopping Cart→tests/features/shopping_cart/. - Scenario Outline → decorators:
<placeholder>columns become@given()parameters with inferred Hypothesis strategies. Example rows become@example()decorators.
TDD workflow
pytest --collect-only→ stubs generated with@pytest.mark.skip- Remove
@pytest.mark.skip, write the test body → test runs and fails (red) - Fix the implementation → test passes (green)
- Add new scenarios to
.featurefiles → only new stubs get the skip marker
Configuration
All configuration lives under [tool.beehave] in pyproject.toml:
[tool.beehave]
features_dir = "docs/features" # default: docs/features
tests_dir = "tests/features" # default: tests/features
default_strategy = "text" # default: text (Hypothesis strategy for placeholders)
background_check_numeric = true # default: true
background_check_string = true # default: true
If features_dir does not exist, the plugin exits silently.
Requirements
| Version | |
|---|---|
| Python | >= 3.14 |
| pytest | >= 6.0 |
| beehave | >= 1.0.0 |
Contributing
git clone https://github.com/nullhack/pytest-beehave
cd pytest-beehave
uv sync --all-extras
uv run task test && uv run task lint && uv run task static-check
Bug reports and pull requests welcome on GitHub.
License
MIT — see LICENSE.
Metadata
Release files for pytest-beehave 1.0.0
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | Provenance | Actions |
|---|---|---|---|---|
| pytest_beehave-1.0.0.tar.gz | 13.3 kB | With provenance | Details for pytest_beehave-1.0.0.tar.gz |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Actions Reset |
|---|---|---|---|---|
| pytest_beehave-1.0.0-py3-none-any.whl | Python 3 | none | any | Details for pytest_beehave-1.0.0-py3-none-any.whl |
Total release size: 23.5 kB
Release files / pytest_beehave-1.0.0.tar.gz
| Download URL | pytest_beehave-1.0.0.tar.gz |
|---|---|
| Size | 13.3 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
e2d745027a58619a06568b3bdb2f88986be6bb4a863e2c5c6fe1a2824cdf2c71
|
|
BLAKE2b-256 checksum How to use checksums |
85295137c2909394ae2933e5be6aa034a7584c9e7e66b7731e46042408f7e6e4
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/6.1.0 CPython/3.13.12
|
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on May 20, 2026.
Transparency logRelease files / pytest_beehave-1.0.0-py3-none-any.whl
| Download URL | pytest_beehave-1.0.0-py3-none-any.whl |
|---|---|
| Size | 10.2 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
2cbcfdf44fc6f328590e324901388731596f0d4c2ab402a593a79363e6b1bd8d
|
|
BLAKE2b-256 checksum How to use checksums |
8d606c0e9ca196f6634a0cc1654b6ea0f46e41269ad3b5868ceae241ed901e20
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/6.1.0 CPython/3.13.12
|
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on May 20, 2026.
Transparency logSecurity for pytest-beehave 1.0.0
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
Full provenance
All 2 files in this release have been uploaded with provenance metadata.
Attestation for pytest_beehave-1.0.0.tar.gz
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on May 20, 2026.
Transparency logA note on package security
PyPI is a repository for distributing packages, not a service for auditing them. While third-party security researchers and our staff do their best to detect and remove malicious packages, PyPI cannot guarantee the quality, security or suitability of any given package.
To learn more about protecting yourself from malicious actors, visit our package security documentation.