Skip to main content

A pytest plugin that generates test stubs from Gherkin feature files, checks consistency, and displays BDD steps in pytest output

Project description

pytest-beehave

Python License PyPI CI

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 Ratetest_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 Carttests/features/shopping_cart/.
  • Scenario Outline → decorators: <placeholder> columns become @given() parameters with inferred Hypothesis strategies. Example rows become @example() decorators.

TDD workflow

  1. pytest --collect-only → stubs generated with @pytest.mark.skip
  2. Remove @pytest.mark.skip, write the test body → test runs and fails (red)
  3. Fix the implementation → test passes (green)
  4. Add new scenarios to .feature files → 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 >= 0.4.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.

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

pytest_beehave-0.2.2.tar.gz (12.9 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

pytest_beehave-0.2.2-py3-none-any.whl (10.0 kB view details)

Uploaded Python 3

File details

Details for the file pytest_beehave-0.2.2.tar.gz.

File metadata

  • Download URL: pytest_beehave-0.2.2.tar.gz
  • Upload date:
  • Size: 12.9 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/6.1.0 CPython/3.13.12

File hashes

Hashes for pytest_beehave-0.2.2.tar.gz
Algorithm Hash digest
SHA256 e90a165c8b6853c545bbea971536ff4c81710623fe89bc0cd3e4f7c29a5f3406
MD5 4966042fa4e41cb2e9799bffb5529831
BLAKE2b-256 c3ea6ac241d175e595f79403a4d1d8b8413c3cfb21dedfe81e6cd29a7d5b854c

See more details on using hashes here.

Provenance

The following attestation bundles were made for pytest_beehave-0.2.2.tar.gz:

Publisher: pypi-publish.yml on nullhack/pytest-beehave

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file pytest_beehave-0.2.2-py3-none-any.whl.

File metadata

  • Download URL: pytest_beehave-0.2.2-py3-none-any.whl
  • Upload date:
  • Size: 10.0 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/6.1.0 CPython/3.13.12

File hashes

Hashes for pytest_beehave-0.2.2-py3-none-any.whl
Algorithm Hash digest
SHA256 f0e9ab4bb5cc3d0abf5ad52d553b3762f42172db89fae40425bfb6882147ea12
MD5 03047170ee42b110ae80d5b44b6aba3e
BLAKE2b-256 cd563d3cc2130d8cf800be084549f7c32351cb3ea5817ca865dae4098bee7fec

See more details on using hashes here.

Provenance

The following attestation bundles were made for pytest_beehave-0.2.2-py3-none-any.whl:

Publisher: pypi-publish.yml on nullhack/pytest-beehave

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page