Priority-based execution for Behave BDD. Execute scenarios ordered by priority, with fail-fast and smoke-first support.
Behave executes scenarios in file order. There is no way to:
- Run critical tests first
- Stop after N failures (intelligent fail-fast)
- Run smoke tests before regression
- Guarantee coverage when time is limited
In CI, if critical tests fail, you waste time waiting for the full regression suite to finish.
behave-priority reorders scenario execution by priority tags and provides fail-fast controls — all configured programmatically in environment.py, no CLI flags needed.
@priority(1)tag on scenarios — lower number = higher priority@feature-priority(1)at feature level — applies to all scenarios in the feature- Scenario-level
@priority(N)overrides feature-level priority - Scenarios without priority tag default to lowest priority (executed last)
order=True— executes scenarios from highest to lowest prioritypriority_tag="smoke"— executes scenarios with that tag first, then the restreverse=True— executes lowest priority first (useful for debugging)
stop_after_failures=N— stops execution after N failed scenariosstop_on_critical=True— stops if any@criticalscenario fails- Combines with
order=True: run critical first, stop if they fail, skip regression
When running with behave --parallel=N, each worker is a separate process. By default, fail-fast is per-worker only. To coordinate fail-fast across all workers:
- Set the
BEHAVE_PRIORITY_COORD_DIRenvironment variable to a shared directory path - Pass
parallel_coord=Truetosetup_priority
export BEHAVE_PRIORITY_COORD_DIR=/tmp/behave_priority_coord
behave --parallel=4setup_priority(
context,
order=True,
stop_after_failures=3,
parallel_coord=True,
)Each worker writes its failure state to a JSON file in the coordination directory. stop_after_failures and stop_on_critical are evaluated globally across all workers. Call cleanup_parallel_coord(context) in after_all to remove the worker's file.
report=True— prints execution order with priorities and timingreport_format="text"(default) — human-readable table with summaryreport_format="json"— machine-readable JSON with entries and summaryreport_format="csv"— CSV with one row per scenario entry- Shows: scenario name, priority value, status (passed/failed/skipped), duration
- Summary: how many critical passed, how many failed, total time saved by fail-fast
time_savedestimation uses priority-bucketed averages (scenarios grouped by priority range 0-99, 100-199, etc.)
Example with JSON output for CI/CD integration:
setup_priority(
context,
order=True,
report=True,
report_format="json",
)pip install behave-priorityFor development:
pip install -e ".[dev]"In your features/environment.py:
from behave_priority import (
setup_priority,
before_scenario_hook,
after_scenario_hook,
priority_report,
)
def before_all(context):
setup_priority(
context,
order=True,
stop_after_failures=3,
stop_on_critical=True,
report=True,
)
def before_scenario(context, scenario):
before_scenario_hook(context, scenario)
def after_scenario(context, scenario):
after_scenario_hook(context, scenario)
def after_all(context):
priority_report(context)In your .feature files:
Feature: User authentication
@priority(1)
@critical
Scenario: Login with valid credentials
Given a registered user
When the user logs in
Then the user should be authenticated
@priority(2)
Scenario: Login with invalid password
Given a registered user
When the user logs in with wrong password
Then the login should fail
@priority(5)
Scenario: Remember me checkbox
Given a registered user
When the user checks remember me
Then the session should persistConfigures priority execution in before_all. All parameters are optional.
| Parameter | Type | Default | Description |
|---|---|---|---|
order |
bool |
False |
Sort scenarios by priority |
reverse |
bool |
False |
Reverse sort order (lowest priority first) |
priority_tag |
str | None |
None |
Tag name to run first (e.g. "smoke") |
stop_after_failures |
int | None |
None |
Stop after N failures |
stop_on_critical |
bool |
False |
Stop if any @critical scenario fails |
critical_tag |
str |
"critical" |
Tag name for critical scenarios |
default_priority |
int |
999 |
Priority for untagged scenarios |
report |
bool |
False |
Print execution report after run |
report_format |
"text" | "json" | "csv" |
"text" |
Output format for the report |
parallel_coord |
bool |
False |
Enable cross-process fail-fast via BEHAVE_PRIORITY_COORD_DIR |
before_scenario_hook(context, scenario)— skips scenario if fail-fast triggeredafter_scenario_hook(context, scenario)— records result, checks fail-fastpriority_report(context)— prints execution report inafter_allcleanup_parallel_coord(context)— removes worker file from coordination directory inafter_all
Immutable frozen dataclass with all configuration options. Can be constructed directly for advanced use cases.
parse_priority(tags) -> int | None— parse@priority(N)from a tag listparse_feature_priority(tags) -> int | None— parse@feature-priority(N)from a tag listresolve_priority(scenario_tags, feature_tags, config, rule_tags=None) -> int— resolve effective priority (scenario > rule > feature > default)is_critical(tags, critical_tag) -> bool— check if scenario is critical
PriorityError— base exception for all behave-priority errorsPriorityParseError— raised when a priority tag has invalid syntax
behave_priority/
├── __init__.py # Public exports
├── exceptions.py # PriorityError, PriorityParseError
├── config.py # PriorityConfig (frozen dataclass)
├── parser.py # Tag priority parsing
├── sorter.py # ScenarioSorter — reorders behave's runner
├── hooks.py # setup_priority, hook functions, PriorityState
├── parallel.py # ParallelCoordinator — cross-process fail-fast
└── report.py # PriorityReport, ReportEntry, ReportSummary
before_all:setup_priority()reads config, sorts features and scenarios by prioritybefore_scenario: hook skips scenario if fail-fast was triggeredafter_scenario: hook records result, checks fail-fast conditionsafter_all:priority_report()prints execution report if enabled
- CI critical-first: Run
@priority(1)scenarios first. If any fail, stop immediately. Don't waste 20 minutes on regression. - Smoke tests: Tag smoke tests
@priority(1) @critical, run withstop_on_critical=True. Get smoke results in 30 seconds. - Time-limited runs: In PR pipelines with time budget,
order=Trueensures most important tests run first. - Debugging:
reverse=Trueruns obscure/edge-case tests first while you're fresh.
- Python >= 3.11
- behave >= 1.2.6
# Install in development mode
pip install -e ".[dev]"
# Run tests
pytest
# Lint
ruff check behave_priority/ tests/
# Type check
mypy --strict behave_priority/
# Coverage
pytest --cov=behave_priority --cov-report=term-missingWhen behave runs with --parallel=N, each worker process gets its own
isolated PriorityState. This has the following consequences:
- Scenario reordering: Each worker sorts only its own subset of
scenarios. Global priority ordering across workers is not guaranteed.
A
@priority(1)scenario assigned to worker 2 may run after a@priority(5)scenario in worker 1. - Fail-fast (
stop_after_failures): By default, only stops scenarios within the same worker. Withparallel_coord=TrueandBEHAVE_PRIORITY_COORD_DIRset, failure counts are aggregated globally across all workers. See Parallel coordination. - Critical stop (
stop_on_critical): By default per-worker only. Withparallel_coord=True, a critical failure in any worker triggers stop in all workers. - Counters:
failed_count,executed_count,critical_failed, andshould_stopare all per-process. The final report reflects only the worker that generated it. - Reports: Generated independently per worker. Each worker prints its own report covering only the scenarios it executed. There is no merged or aggregated report.
time_savedestimation: Inaccurate in parallel mode. The estimation assumes sequential execution; with N workers, skipped scenarios in one worker overlap with execution in others.priority_tag: Scenarios matching the priority tag are sorted first within each worker, but not globally across workers.