behave-priority 1.0.0__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- behave_priority-1.0.0/.gitignore +20 -0
- behave_priority-1.0.0/LICENSE +21 -0
- behave_priority-1.0.0/PKG-INFO +325 -0
- behave_priority-1.0.0/README.md +286 -0
- behave_priority-1.0.0/behave_priority/__init__.py +59 -0
- behave_priority-1.0.0/behave_priority/config.py +71 -0
- behave_priority-1.0.0/behave_priority/exceptions.py +15 -0
- behave_priority-1.0.0/behave_priority/hooks.py +390 -0
- behave_priority-1.0.0/behave_priority/parallel.py +164 -0
- behave_priority-1.0.0/behave_priority/parser.py +132 -0
- behave_priority-1.0.0/behave_priority/report.py +398 -0
- behave_priority-1.0.0/behave_priority/sorter.py +249 -0
- behave_priority-1.0.0/docs/conf.py +60 -0
- behave_priority-1.0.0/docs/guides/configuration.rst +179 -0
- behave_priority-1.0.0/docs/guides/gherkin_v6.rst +131 -0
- behave_priority-1.0.0/docs/guides/integration.rst +151 -0
- behave_priority-1.0.0/docs/guides/reports.rst +181 -0
- behave_priority-1.0.0/docs/index.rst +293 -0
- behave_priority-1.0.0/docs/modules/config.rst +134 -0
- behave_priority-1.0.0/docs/modules/exceptions.rst +58 -0
- behave_priority-1.0.0/docs/modules/hooks.rst +189 -0
- behave_priority-1.0.0/docs/modules/parallel.rst +24 -0
- behave_priority-1.0.0/docs/modules/parser.rst +135 -0
- behave_priority-1.0.0/docs/modules/report.rst +206 -0
- behave_priority-1.0.0/docs/modules/sorter.rst +111 -0
- behave_priority-1.0.0/docs/requirements.txt +3 -0
- behave_priority-1.0.0/pyproject.toml +93 -0
- behave_priority-1.0.0/tests/__init__.py +0 -0
- behave_priority-1.0.0/tests/conftest.py +1 -0
- behave_priority-1.0.0/tests/e2e/__init__.py +0 -0
- behave_priority-1.0.0/tests/e2e/environment.py +61 -0
- behave_priority-1.0.0/tests/e2e/features/critical.feature +21 -0
- behave_priority-1.0.0/tests/e2e/features/failfast.feature +25 -0
- behave_priority-1.0.0/tests/e2e/features/feature_priority.feature +13 -0
- behave_priority-1.0.0/tests/e2e/features/outline.feature +19 -0
- behave_priority-1.0.0/tests/e2e/features/priority_order.feature +25 -0
- behave_priority-1.0.0/tests/e2e/features/rule_priority.feature +23 -0
- behave_priority-1.0.0/tests/e2e/features/smoke_tag.feature +18 -0
- behave_priority-1.0.0/tests/e2e/steps/steps.py +28 -0
- behave_priority-1.0.0/tests/e2e/test_e2e.py +241 -0
- behave_priority-1.0.0/tests/integration/__init__.py +0 -0
- behave_priority-1.0.0/tests/integration/conftest.py +156 -0
- behave_priority-1.0.0/tests/integration/test_failfast.py +83 -0
- behave_priority-1.0.0/tests/integration/test_no_config.py +53 -0
- behave_priority-1.0.0/tests/integration/test_order.py +107 -0
- behave_priority-1.0.0/tests/integration/test_report.py +95 -0
- behave_priority-1.0.0/tests/integration/test_smoke_tag.py +77 -0
- behave_priority-1.0.0/tests/unit/__init__.py +0 -0
- behave_priority-1.0.0/tests/unit/test_config.py +234 -0
- behave_priority-1.0.0/tests/unit/test_exceptions.py +68 -0
- behave_priority-1.0.0/tests/unit/test_hooks.py +818 -0
- behave_priority-1.0.0/tests/unit/test_parallel.py +185 -0
- behave_priority-1.0.0/tests/unit/test_parser.py +330 -0
- behave_priority-1.0.0/tests/unit/test_report.py +713 -0
- behave_priority-1.0.0/tests/unit/test_smoke.py +40 -0
- behave_priority-1.0.0/tests/unit/test_sorter.py +730 -0
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2025 Mathias Paulenko
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
|
@@ -0,0 +1,325 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: behave-priority
|
|
3
|
+
Version: 1.0.0
|
|
4
|
+
Summary: Priority-based execution for Behave BDD
|
|
5
|
+
Project-URL: Homepage, https://github.com/MathiasPaulenko/behave-priority
|
|
6
|
+
Project-URL: Repository, https://github.com/MathiasPaulenko/behave-priority
|
|
7
|
+
Project-URL: Issues, https://github.com/MathiasPaulenko/behave-priority/issues
|
|
8
|
+
Project-URL: Changelog, https://github.com/MathiasPaulenko/behave-priority/releases
|
|
9
|
+
Author-email: Mathias Paulenko <mathias.paulenko@gmail.com>
|
|
10
|
+
License-Expression: MIT
|
|
11
|
+
License-File: LICENSE
|
|
12
|
+
Keywords: bdd,behave,cucumber,fail-fast,gherkin,priority,testing
|
|
13
|
+
Classifier: Development Status :: 5 - Production/Stable
|
|
14
|
+
Classifier: Framework :: Pytest
|
|
15
|
+
Classifier: Intended Audience :: Developers
|
|
16
|
+
Classifier: License :: OSI Approved :: MIT License
|
|
17
|
+
Classifier: Operating System :: OS Independent
|
|
18
|
+
Classifier: Programming Language :: Python :: 3
|
|
19
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
20
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
21
|
+
Classifier: Programming Language :: Python :: 3.13
|
|
22
|
+
Classifier: Programming Language :: Python :: Implementation :: CPython
|
|
23
|
+
Classifier: Topic :: Software Development :: Testing
|
|
24
|
+
Classifier: Topic :: Software Development :: Testing :: BDD
|
|
25
|
+
Classifier: Typing :: Typed
|
|
26
|
+
Requires-Python: >=3.11
|
|
27
|
+
Requires-Dist: behave>=1.2.6
|
|
28
|
+
Provides-Extra: dev
|
|
29
|
+
Requires-Dist: build>=1.0; extra == 'dev'
|
|
30
|
+
Requires-Dist: mypy>=1.10; extra == 'dev'
|
|
31
|
+
Requires-Dist: pytest-cov>=5.0; extra == 'dev'
|
|
32
|
+
Requires-Dist: pytest>=8.0; extra == 'dev'
|
|
33
|
+
Requires-Dist: ruff>=0.5; extra == 'dev'
|
|
34
|
+
Provides-Extra: docs
|
|
35
|
+
Requires-Dist: furo>=2024.0; extra == 'docs'
|
|
36
|
+
Requires-Dist: sphinx-autodoc-typehints>=2.0; extra == 'docs'
|
|
37
|
+
Requires-Dist: sphinx>=7.0; extra == 'docs'
|
|
38
|
+
Description-Content-Type: text/markdown
|
|
39
|
+
|
|
40
|
+
# behave-priority
|
|
41
|
+
|
|
42
|
+
[](https://www.python.org/downloads/)
|
|
43
|
+
[](LICENSE)
|
|
44
|
+
[]()
|
|
45
|
+
[]()
|
|
46
|
+
[]()
|
|
47
|
+
[]()
|
|
48
|
+
|
|
49
|
+
Priority-based execution for Behave BDD. Execute scenarios ordered by priority, with fail-fast and smoke-first support.
|
|
50
|
+
|
|
51
|
+
## Problem
|
|
52
|
+
|
|
53
|
+
Behave executes scenarios in file order. There is no way to:
|
|
54
|
+
|
|
55
|
+
- Run critical tests first
|
|
56
|
+
- Stop after N failures (intelligent fail-fast)
|
|
57
|
+
- Run smoke tests before regression
|
|
58
|
+
- Guarantee coverage when time is limited
|
|
59
|
+
|
|
60
|
+
In CI, if critical tests fail, you waste time waiting for the full regression suite to finish.
|
|
61
|
+
|
|
62
|
+
## Solution
|
|
63
|
+
|
|
64
|
+
`behave-priority` reorders scenario execution by priority tags and provides fail-fast controls — all configured programmatically in `environment.py`, no CLI flags needed.
|
|
65
|
+
|
|
66
|
+
## Features
|
|
67
|
+
|
|
68
|
+
### Priority tags
|
|
69
|
+
|
|
70
|
+
- `@priority(1)` tag on scenarios — lower number = higher priority
|
|
71
|
+
- `@feature-priority(1)` at feature level — applies to all scenarios in the feature
|
|
72
|
+
- Scenario-level `@priority(N)` overrides feature-level priority
|
|
73
|
+
- Scenarios without priority tag default to lowest priority (executed last)
|
|
74
|
+
|
|
75
|
+
### Execution ordering
|
|
76
|
+
|
|
77
|
+
- `order=True` — executes scenarios from highest to lowest priority
|
|
78
|
+
- `priority_tag="smoke"` — executes scenarios with that tag first, then the rest
|
|
79
|
+
- `reverse=True` — executes lowest priority first (useful for debugging)
|
|
80
|
+
|
|
81
|
+
### Fail-fast
|
|
82
|
+
|
|
83
|
+
- `stop_after_failures=N` — stops execution after N failed scenarios
|
|
84
|
+
- `stop_on_critical=True` — stops if any `@critical` scenario fails
|
|
85
|
+
- Combines with `order=True`: run critical first, stop if they fail, skip regression
|
|
86
|
+
|
|
87
|
+
### Parallel coordination
|
|
88
|
+
|
|
89
|
+
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:
|
|
90
|
+
|
|
91
|
+
1. Set the `BEHAVE_PRIORITY_COORD_DIR` environment variable to a shared directory path
|
|
92
|
+
2. Pass `parallel_coord=True` to `setup_priority`
|
|
93
|
+
|
|
94
|
+
```bash
|
|
95
|
+
export BEHAVE_PRIORITY_COORD_DIR=/tmp/behave_priority_coord
|
|
96
|
+
behave --parallel=4
|
|
97
|
+
```
|
|
98
|
+
|
|
99
|
+
```python
|
|
100
|
+
setup_priority(
|
|
101
|
+
context,
|
|
102
|
+
order=True,
|
|
103
|
+
stop_after_failures=3,
|
|
104
|
+
parallel_coord=True,
|
|
105
|
+
)
|
|
106
|
+
```
|
|
107
|
+
|
|
108
|
+
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.
|
|
109
|
+
|
|
110
|
+
### Reporting
|
|
111
|
+
|
|
112
|
+
- `report=True` — prints execution order with priorities and timing
|
|
113
|
+
- `report_format="text"` (default) — human-readable table with summary
|
|
114
|
+
- `report_format="json"` — machine-readable JSON with entries and summary
|
|
115
|
+
- `report_format="csv"` — CSV with one row per scenario entry
|
|
116
|
+
- Shows: scenario name, priority value, status (passed/failed/skipped), duration
|
|
117
|
+
- Summary: how many critical passed, how many failed, total time saved by fail-fast
|
|
118
|
+
- `time_saved` estimation uses priority-bucketed averages (scenarios grouped by priority range 0-99, 100-199, etc.)
|
|
119
|
+
|
|
120
|
+
Example with JSON output for CI/CD integration:
|
|
121
|
+
|
|
122
|
+
```python
|
|
123
|
+
setup_priority(
|
|
124
|
+
context,
|
|
125
|
+
order=True,
|
|
126
|
+
report=True,
|
|
127
|
+
report_format="json",
|
|
128
|
+
)
|
|
129
|
+
```
|
|
130
|
+
|
|
131
|
+
## Installation
|
|
132
|
+
|
|
133
|
+
```bash
|
|
134
|
+
pip install behave-priority
|
|
135
|
+
```
|
|
136
|
+
|
|
137
|
+
For development:
|
|
138
|
+
|
|
139
|
+
```bash
|
|
140
|
+
pip install -e ".[dev]"
|
|
141
|
+
```
|
|
142
|
+
|
|
143
|
+
## Quick start
|
|
144
|
+
|
|
145
|
+
In your `features/environment.py`:
|
|
146
|
+
|
|
147
|
+
```python
|
|
148
|
+
from behave_priority import (
|
|
149
|
+
setup_priority,
|
|
150
|
+
before_scenario_hook,
|
|
151
|
+
after_scenario_hook,
|
|
152
|
+
priority_report,
|
|
153
|
+
)
|
|
154
|
+
|
|
155
|
+
def before_all(context):
|
|
156
|
+
setup_priority(
|
|
157
|
+
context,
|
|
158
|
+
order=True,
|
|
159
|
+
stop_after_failures=3,
|
|
160
|
+
stop_on_critical=True,
|
|
161
|
+
report=True,
|
|
162
|
+
)
|
|
163
|
+
|
|
164
|
+
def before_scenario(context, scenario):
|
|
165
|
+
before_scenario_hook(context, scenario)
|
|
166
|
+
|
|
167
|
+
def after_scenario(context, scenario):
|
|
168
|
+
after_scenario_hook(context, scenario)
|
|
169
|
+
|
|
170
|
+
def after_all(context):
|
|
171
|
+
priority_report(context)
|
|
172
|
+
```
|
|
173
|
+
|
|
174
|
+
In your `.feature` files:
|
|
175
|
+
|
|
176
|
+
```gherkin
|
|
177
|
+
Feature: User authentication
|
|
178
|
+
|
|
179
|
+
@priority(1)
|
|
180
|
+
@critical
|
|
181
|
+
Scenario: Login with valid credentials
|
|
182
|
+
Given a registered user
|
|
183
|
+
When the user logs in
|
|
184
|
+
Then the user should be authenticated
|
|
185
|
+
|
|
186
|
+
@priority(2)
|
|
187
|
+
Scenario: Login with invalid password
|
|
188
|
+
Given a registered user
|
|
189
|
+
When the user logs in with wrong password
|
|
190
|
+
Then the login should fail
|
|
191
|
+
|
|
192
|
+
@priority(5)
|
|
193
|
+
Scenario: Remember me checkbox
|
|
194
|
+
Given a registered user
|
|
195
|
+
When the user checks remember me
|
|
196
|
+
Then the session should persist
|
|
197
|
+
```
|
|
198
|
+
|
|
199
|
+
## API reference
|
|
200
|
+
|
|
201
|
+
### `setup_priority(context, **kwargs)`
|
|
202
|
+
|
|
203
|
+
Configures priority execution in `before_all`. All parameters are optional.
|
|
204
|
+
|
|
205
|
+
| Parameter | Type | Default | Description |
|
|
206
|
+
|---|---|---|---|
|
|
207
|
+
| `order` | `bool` | `False` | Sort scenarios by priority |
|
|
208
|
+
| `reverse` | `bool` | `False` | Reverse sort order (lowest priority first) |
|
|
209
|
+
| `priority_tag` | `str \| None` | `None` | Tag name to run first (e.g. `"smoke"`) |
|
|
210
|
+
| `stop_after_failures` | `int \| None` | `None` | Stop after N failures |
|
|
211
|
+
| `stop_on_critical` | `bool` | `False` | Stop if any `@critical` scenario fails |
|
|
212
|
+
| `critical_tag` | `str` | `"critical"` | Tag name for critical scenarios |
|
|
213
|
+
| `default_priority` | `int` | `999` | Priority for untagged scenarios |
|
|
214
|
+
| `report` | `bool` | `False` | Print execution report after run |
|
|
215
|
+
| `report_format` | `"text" \| "json" \| "csv"` | `"text"` | Output format for the report |
|
|
216
|
+
| `parallel_coord` | `bool` | `False` | Enable cross-process fail-fast via `BEHAVE_PRIORITY_COORD_DIR` |
|
|
217
|
+
|
|
218
|
+
### Hook functions
|
|
219
|
+
|
|
220
|
+
- `before_scenario_hook(context, scenario)` — skips scenario if fail-fast triggered
|
|
221
|
+
- `after_scenario_hook(context, scenario)` — records result, checks fail-fast
|
|
222
|
+
- `priority_report(context)` — prints execution report in `after_all`
|
|
223
|
+
- `cleanup_parallel_coord(context)` — removes worker file from coordination directory in `after_all`
|
|
224
|
+
|
|
225
|
+
### `PriorityConfig`
|
|
226
|
+
|
|
227
|
+
Immutable frozen dataclass with all configuration options. Can be constructed directly for advanced use cases.
|
|
228
|
+
|
|
229
|
+
### Parser functions
|
|
230
|
+
|
|
231
|
+
- `parse_priority(tags) -> int | None` — parse `@priority(N)` from a tag list
|
|
232
|
+
- `parse_feature_priority(tags) -> int | None` — parse `@feature-priority(N)` from a tag list
|
|
233
|
+
- `resolve_priority(scenario_tags, feature_tags, config, rule_tags=None) -> int` — resolve effective priority (scenario > rule > feature > default)
|
|
234
|
+
- `is_critical(tags, critical_tag) -> bool` — check if scenario is critical
|
|
235
|
+
|
|
236
|
+
### Exceptions
|
|
237
|
+
|
|
238
|
+
- `PriorityError` — base exception for all behave-priority errors
|
|
239
|
+
- `PriorityParseError` — raised when a priority tag has invalid syntax
|
|
240
|
+
|
|
241
|
+
## Architecture
|
|
242
|
+
|
|
243
|
+
```
|
|
244
|
+
behave_priority/
|
|
245
|
+
├── __init__.py # Public exports
|
|
246
|
+
├── exceptions.py # PriorityError, PriorityParseError
|
|
247
|
+
├── config.py # PriorityConfig (frozen dataclass)
|
|
248
|
+
├── parser.py # Tag priority parsing
|
|
249
|
+
├── sorter.py # ScenarioSorter — reorders behave's runner
|
|
250
|
+
├── hooks.py # setup_priority, hook functions, PriorityState
|
|
251
|
+
├── parallel.py # ParallelCoordinator — cross-process fail-fast
|
|
252
|
+
└── report.py # PriorityReport, ReportEntry, ReportSummary
|
|
253
|
+
```
|
|
254
|
+
|
|
255
|
+
### How it works
|
|
256
|
+
|
|
257
|
+
1. **`before_all`**: `setup_priority()` reads config, sorts features and scenarios by priority
|
|
258
|
+
2. **`before_scenario`**: hook skips scenario if fail-fast was triggered
|
|
259
|
+
3. **`after_scenario`**: hook records result, checks fail-fast conditions
|
|
260
|
+
4. **`after_all`**: `priority_report()` prints execution report if enabled
|
|
261
|
+
|
|
262
|
+
## Use cases
|
|
263
|
+
|
|
264
|
+
1. **CI critical-first**: Run `@priority(1)` scenarios first. If any fail, stop immediately. Don't waste 20 minutes on regression.
|
|
265
|
+
2. **Smoke tests**: Tag smoke tests `@priority(1) @critical`, run with `stop_on_critical=True`. Get smoke results in 30 seconds.
|
|
266
|
+
3. **Time-limited runs**: In PR pipelines with time budget, `order=True` ensures most important tests run first.
|
|
267
|
+
4. **Debugging**: `reverse=True` runs obscure/edge-case tests first while you're fresh.
|
|
268
|
+
|
|
269
|
+
## Requirements
|
|
270
|
+
|
|
271
|
+
- Python >= 3.11
|
|
272
|
+
- behave >= 1.2.6
|
|
273
|
+
|
|
274
|
+
## Development
|
|
275
|
+
|
|
276
|
+
```bash
|
|
277
|
+
# Install in development mode
|
|
278
|
+
pip install -e ".[dev]"
|
|
279
|
+
|
|
280
|
+
# Run tests
|
|
281
|
+
pytest
|
|
282
|
+
|
|
283
|
+
# Lint
|
|
284
|
+
ruff check behave_priority/ tests/
|
|
285
|
+
|
|
286
|
+
# Type check
|
|
287
|
+
mypy --strict behave_priority/
|
|
288
|
+
|
|
289
|
+
# Coverage
|
|
290
|
+
pytest --cov=behave_priority --cov-report=term-missing
|
|
291
|
+
```
|
|
292
|
+
|
|
293
|
+
## Limitations
|
|
294
|
+
|
|
295
|
+
### Parallel execution (`--parallel`)
|
|
296
|
+
|
|
297
|
+
When behave runs with `--parallel=N`, each worker process gets its own
|
|
298
|
+
isolated `PriorityState`. This has the following consequences:
|
|
299
|
+
|
|
300
|
+
- **Scenario reordering**: Each worker sorts only its own subset of
|
|
301
|
+
scenarios. Global priority ordering across workers is not guaranteed.
|
|
302
|
+
A `@priority(1)` scenario assigned to worker 2 may run after a
|
|
303
|
+
`@priority(5)` scenario in worker 1.
|
|
304
|
+
- **Fail-fast (`stop_after_failures`)**: By default, only stops scenarios
|
|
305
|
+
within the same worker. With `parallel_coord=True` and
|
|
306
|
+
`BEHAVE_PRIORITY_COORD_DIR` set, failure counts are aggregated globally
|
|
307
|
+
across all workers. See [Parallel coordination](#parallel-coordination).
|
|
308
|
+
- **Critical stop (`stop_on_critical`)**: By default per-worker only.
|
|
309
|
+
With `parallel_coord=True`, a critical failure in any worker triggers
|
|
310
|
+
stop in all workers.
|
|
311
|
+
- **Counters**: `failed_count`, `executed_count`, `critical_failed`, and
|
|
312
|
+
`should_stop` are all per-process. The final report reflects only the
|
|
313
|
+
worker that generated it.
|
|
314
|
+
- **Reports**: Generated independently per worker. Each worker prints
|
|
315
|
+
its own report covering only the scenarios it executed. There is no
|
|
316
|
+
merged or aggregated report.
|
|
317
|
+
- **`time_saved` estimation**: Inaccurate in parallel mode. The
|
|
318
|
+
estimation assumes sequential execution; with N workers, skipped
|
|
319
|
+
scenarios in one worker overlap with execution in others.
|
|
320
|
+
- **`priority_tag`**: Scenarios matching the priority tag are sorted
|
|
321
|
+
first within each worker, but not globally across workers.
|
|
322
|
+
|
|
323
|
+
## License
|
|
324
|
+
|
|
325
|
+
[MIT](LICENSE)
|
|
@@ -0,0 +1,286 @@
|
|
|
1
|
+
# behave-priority
|
|
2
|
+
|
|
3
|
+
[](https://www.python.org/downloads/)
|
|
4
|
+
[](LICENSE)
|
|
5
|
+
[]()
|
|
6
|
+
[]()
|
|
7
|
+
[]()
|
|
8
|
+
[]()
|
|
9
|
+
|
|
10
|
+
Priority-based execution for Behave BDD. Execute scenarios ordered by priority, with fail-fast and smoke-first support.
|
|
11
|
+
|
|
12
|
+
## Problem
|
|
13
|
+
|
|
14
|
+
Behave executes scenarios in file order. There is no way to:
|
|
15
|
+
|
|
16
|
+
- Run critical tests first
|
|
17
|
+
- Stop after N failures (intelligent fail-fast)
|
|
18
|
+
- Run smoke tests before regression
|
|
19
|
+
- Guarantee coverage when time is limited
|
|
20
|
+
|
|
21
|
+
In CI, if critical tests fail, you waste time waiting for the full regression suite to finish.
|
|
22
|
+
|
|
23
|
+
## Solution
|
|
24
|
+
|
|
25
|
+
`behave-priority` reorders scenario execution by priority tags and provides fail-fast controls — all configured programmatically in `environment.py`, no CLI flags needed.
|
|
26
|
+
|
|
27
|
+
## Features
|
|
28
|
+
|
|
29
|
+
### Priority tags
|
|
30
|
+
|
|
31
|
+
- `@priority(1)` tag on scenarios — lower number = higher priority
|
|
32
|
+
- `@feature-priority(1)` at feature level — applies to all scenarios in the feature
|
|
33
|
+
- Scenario-level `@priority(N)` overrides feature-level priority
|
|
34
|
+
- Scenarios without priority tag default to lowest priority (executed last)
|
|
35
|
+
|
|
36
|
+
### Execution ordering
|
|
37
|
+
|
|
38
|
+
- `order=True` — executes scenarios from highest to lowest priority
|
|
39
|
+
- `priority_tag="smoke"` — executes scenarios with that tag first, then the rest
|
|
40
|
+
- `reverse=True` — executes lowest priority first (useful for debugging)
|
|
41
|
+
|
|
42
|
+
### Fail-fast
|
|
43
|
+
|
|
44
|
+
- `stop_after_failures=N` — stops execution after N failed scenarios
|
|
45
|
+
- `stop_on_critical=True` — stops if any `@critical` scenario fails
|
|
46
|
+
- Combines with `order=True`: run critical first, stop if they fail, skip regression
|
|
47
|
+
|
|
48
|
+
### Parallel coordination
|
|
49
|
+
|
|
50
|
+
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:
|
|
51
|
+
|
|
52
|
+
1. Set the `BEHAVE_PRIORITY_COORD_DIR` environment variable to a shared directory path
|
|
53
|
+
2. Pass `parallel_coord=True` to `setup_priority`
|
|
54
|
+
|
|
55
|
+
```bash
|
|
56
|
+
export BEHAVE_PRIORITY_COORD_DIR=/tmp/behave_priority_coord
|
|
57
|
+
behave --parallel=4
|
|
58
|
+
```
|
|
59
|
+
|
|
60
|
+
```python
|
|
61
|
+
setup_priority(
|
|
62
|
+
context,
|
|
63
|
+
order=True,
|
|
64
|
+
stop_after_failures=3,
|
|
65
|
+
parallel_coord=True,
|
|
66
|
+
)
|
|
67
|
+
```
|
|
68
|
+
|
|
69
|
+
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.
|
|
70
|
+
|
|
71
|
+
### Reporting
|
|
72
|
+
|
|
73
|
+
- `report=True` — prints execution order with priorities and timing
|
|
74
|
+
- `report_format="text"` (default) — human-readable table with summary
|
|
75
|
+
- `report_format="json"` — machine-readable JSON with entries and summary
|
|
76
|
+
- `report_format="csv"` — CSV with one row per scenario entry
|
|
77
|
+
- Shows: scenario name, priority value, status (passed/failed/skipped), duration
|
|
78
|
+
- Summary: how many critical passed, how many failed, total time saved by fail-fast
|
|
79
|
+
- `time_saved` estimation uses priority-bucketed averages (scenarios grouped by priority range 0-99, 100-199, etc.)
|
|
80
|
+
|
|
81
|
+
Example with JSON output for CI/CD integration:
|
|
82
|
+
|
|
83
|
+
```python
|
|
84
|
+
setup_priority(
|
|
85
|
+
context,
|
|
86
|
+
order=True,
|
|
87
|
+
report=True,
|
|
88
|
+
report_format="json",
|
|
89
|
+
)
|
|
90
|
+
```
|
|
91
|
+
|
|
92
|
+
## Installation
|
|
93
|
+
|
|
94
|
+
```bash
|
|
95
|
+
pip install behave-priority
|
|
96
|
+
```
|
|
97
|
+
|
|
98
|
+
For development:
|
|
99
|
+
|
|
100
|
+
```bash
|
|
101
|
+
pip install -e ".[dev]"
|
|
102
|
+
```
|
|
103
|
+
|
|
104
|
+
## Quick start
|
|
105
|
+
|
|
106
|
+
In your `features/environment.py`:
|
|
107
|
+
|
|
108
|
+
```python
|
|
109
|
+
from behave_priority import (
|
|
110
|
+
setup_priority,
|
|
111
|
+
before_scenario_hook,
|
|
112
|
+
after_scenario_hook,
|
|
113
|
+
priority_report,
|
|
114
|
+
)
|
|
115
|
+
|
|
116
|
+
def before_all(context):
|
|
117
|
+
setup_priority(
|
|
118
|
+
context,
|
|
119
|
+
order=True,
|
|
120
|
+
stop_after_failures=3,
|
|
121
|
+
stop_on_critical=True,
|
|
122
|
+
report=True,
|
|
123
|
+
)
|
|
124
|
+
|
|
125
|
+
def before_scenario(context, scenario):
|
|
126
|
+
before_scenario_hook(context, scenario)
|
|
127
|
+
|
|
128
|
+
def after_scenario(context, scenario):
|
|
129
|
+
after_scenario_hook(context, scenario)
|
|
130
|
+
|
|
131
|
+
def after_all(context):
|
|
132
|
+
priority_report(context)
|
|
133
|
+
```
|
|
134
|
+
|
|
135
|
+
In your `.feature` files:
|
|
136
|
+
|
|
137
|
+
```gherkin
|
|
138
|
+
Feature: User authentication
|
|
139
|
+
|
|
140
|
+
@priority(1)
|
|
141
|
+
@critical
|
|
142
|
+
Scenario: Login with valid credentials
|
|
143
|
+
Given a registered user
|
|
144
|
+
When the user logs in
|
|
145
|
+
Then the user should be authenticated
|
|
146
|
+
|
|
147
|
+
@priority(2)
|
|
148
|
+
Scenario: Login with invalid password
|
|
149
|
+
Given a registered user
|
|
150
|
+
When the user logs in with wrong password
|
|
151
|
+
Then the login should fail
|
|
152
|
+
|
|
153
|
+
@priority(5)
|
|
154
|
+
Scenario: Remember me checkbox
|
|
155
|
+
Given a registered user
|
|
156
|
+
When the user checks remember me
|
|
157
|
+
Then the session should persist
|
|
158
|
+
```
|
|
159
|
+
|
|
160
|
+
## API reference
|
|
161
|
+
|
|
162
|
+
### `setup_priority(context, **kwargs)`
|
|
163
|
+
|
|
164
|
+
Configures priority execution in `before_all`. All parameters are optional.
|
|
165
|
+
|
|
166
|
+
| Parameter | Type | Default | Description |
|
|
167
|
+
|---|---|---|---|
|
|
168
|
+
| `order` | `bool` | `False` | Sort scenarios by priority |
|
|
169
|
+
| `reverse` | `bool` | `False` | Reverse sort order (lowest priority first) |
|
|
170
|
+
| `priority_tag` | `str \| None` | `None` | Tag name to run first (e.g. `"smoke"`) |
|
|
171
|
+
| `stop_after_failures` | `int \| None` | `None` | Stop after N failures |
|
|
172
|
+
| `stop_on_critical` | `bool` | `False` | Stop if any `@critical` scenario fails |
|
|
173
|
+
| `critical_tag` | `str` | `"critical"` | Tag name for critical scenarios |
|
|
174
|
+
| `default_priority` | `int` | `999` | Priority for untagged scenarios |
|
|
175
|
+
| `report` | `bool` | `False` | Print execution report after run |
|
|
176
|
+
| `report_format` | `"text" \| "json" \| "csv"` | `"text"` | Output format for the report |
|
|
177
|
+
| `parallel_coord` | `bool` | `False` | Enable cross-process fail-fast via `BEHAVE_PRIORITY_COORD_DIR` |
|
|
178
|
+
|
|
179
|
+
### Hook functions
|
|
180
|
+
|
|
181
|
+
- `before_scenario_hook(context, scenario)` — skips scenario if fail-fast triggered
|
|
182
|
+
- `after_scenario_hook(context, scenario)` — records result, checks fail-fast
|
|
183
|
+
- `priority_report(context)` — prints execution report in `after_all`
|
|
184
|
+
- `cleanup_parallel_coord(context)` — removes worker file from coordination directory in `after_all`
|
|
185
|
+
|
|
186
|
+
### `PriorityConfig`
|
|
187
|
+
|
|
188
|
+
Immutable frozen dataclass with all configuration options. Can be constructed directly for advanced use cases.
|
|
189
|
+
|
|
190
|
+
### Parser functions
|
|
191
|
+
|
|
192
|
+
- `parse_priority(tags) -> int | None` — parse `@priority(N)` from a tag list
|
|
193
|
+
- `parse_feature_priority(tags) -> int | None` — parse `@feature-priority(N)` from a tag list
|
|
194
|
+
- `resolve_priority(scenario_tags, feature_tags, config, rule_tags=None) -> int` — resolve effective priority (scenario > rule > feature > default)
|
|
195
|
+
- `is_critical(tags, critical_tag) -> bool` — check if scenario is critical
|
|
196
|
+
|
|
197
|
+
### Exceptions
|
|
198
|
+
|
|
199
|
+
- `PriorityError` — base exception for all behave-priority errors
|
|
200
|
+
- `PriorityParseError` — raised when a priority tag has invalid syntax
|
|
201
|
+
|
|
202
|
+
## Architecture
|
|
203
|
+
|
|
204
|
+
```
|
|
205
|
+
behave_priority/
|
|
206
|
+
├── __init__.py # Public exports
|
|
207
|
+
├── exceptions.py # PriorityError, PriorityParseError
|
|
208
|
+
├── config.py # PriorityConfig (frozen dataclass)
|
|
209
|
+
├── parser.py # Tag priority parsing
|
|
210
|
+
├── sorter.py # ScenarioSorter — reorders behave's runner
|
|
211
|
+
├── hooks.py # setup_priority, hook functions, PriorityState
|
|
212
|
+
├── parallel.py # ParallelCoordinator — cross-process fail-fast
|
|
213
|
+
└── report.py # PriorityReport, ReportEntry, ReportSummary
|
|
214
|
+
```
|
|
215
|
+
|
|
216
|
+
### How it works
|
|
217
|
+
|
|
218
|
+
1. **`before_all`**: `setup_priority()` reads config, sorts features and scenarios by priority
|
|
219
|
+
2. **`before_scenario`**: hook skips scenario if fail-fast was triggered
|
|
220
|
+
3. **`after_scenario`**: hook records result, checks fail-fast conditions
|
|
221
|
+
4. **`after_all`**: `priority_report()` prints execution report if enabled
|
|
222
|
+
|
|
223
|
+
## Use cases
|
|
224
|
+
|
|
225
|
+
1. **CI critical-first**: Run `@priority(1)` scenarios first. If any fail, stop immediately. Don't waste 20 minutes on regression.
|
|
226
|
+
2. **Smoke tests**: Tag smoke tests `@priority(1) @critical`, run with `stop_on_critical=True`. Get smoke results in 30 seconds.
|
|
227
|
+
3. **Time-limited runs**: In PR pipelines with time budget, `order=True` ensures most important tests run first.
|
|
228
|
+
4. **Debugging**: `reverse=True` runs obscure/edge-case tests first while you're fresh.
|
|
229
|
+
|
|
230
|
+
## Requirements
|
|
231
|
+
|
|
232
|
+
- Python >= 3.11
|
|
233
|
+
- behave >= 1.2.6
|
|
234
|
+
|
|
235
|
+
## Development
|
|
236
|
+
|
|
237
|
+
```bash
|
|
238
|
+
# Install in development mode
|
|
239
|
+
pip install -e ".[dev]"
|
|
240
|
+
|
|
241
|
+
# Run tests
|
|
242
|
+
pytest
|
|
243
|
+
|
|
244
|
+
# Lint
|
|
245
|
+
ruff check behave_priority/ tests/
|
|
246
|
+
|
|
247
|
+
# Type check
|
|
248
|
+
mypy --strict behave_priority/
|
|
249
|
+
|
|
250
|
+
# Coverage
|
|
251
|
+
pytest --cov=behave_priority --cov-report=term-missing
|
|
252
|
+
```
|
|
253
|
+
|
|
254
|
+
## Limitations
|
|
255
|
+
|
|
256
|
+
### Parallel execution (`--parallel`)
|
|
257
|
+
|
|
258
|
+
When behave runs with `--parallel=N`, each worker process gets its own
|
|
259
|
+
isolated `PriorityState`. This has the following consequences:
|
|
260
|
+
|
|
261
|
+
- **Scenario reordering**: Each worker sorts only its own subset of
|
|
262
|
+
scenarios. Global priority ordering across workers is not guaranteed.
|
|
263
|
+
A `@priority(1)` scenario assigned to worker 2 may run after a
|
|
264
|
+
`@priority(5)` scenario in worker 1.
|
|
265
|
+
- **Fail-fast (`stop_after_failures`)**: By default, only stops scenarios
|
|
266
|
+
within the same worker. With `parallel_coord=True` and
|
|
267
|
+
`BEHAVE_PRIORITY_COORD_DIR` set, failure counts are aggregated globally
|
|
268
|
+
across all workers. See [Parallel coordination](#parallel-coordination).
|
|
269
|
+
- **Critical stop (`stop_on_critical`)**: By default per-worker only.
|
|
270
|
+
With `parallel_coord=True`, a critical failure in any worker triggers
|
|
271
|
+
stop in all workers.
|
|
272
|
+
- **Counters**: `failed_count`, `executed_count`, `critical_failed`, and
|
|
273
|
+
`should_stop` are all per-process. The final report reflects only the
|
|
274
|
+
worker that generated it.
|
|
275
|
+
- **Reports**: Generated independently per worker. Each worker prints
|
|
276
|
+
its own report covering only the scenarios it executed. There is no
|
|
277
|
+
merged or aggregated report.
|
|
278
|
+
- **`time_saved` estimation**: Inaccurate in parallel mode. The
|
|
279
|
+
estimation assumes sequential execution; with N workers, skipped
|
|
280
|
+
scenarios in one worker overlap with execution in others.
|
|
281
|
+
- **`priority_tag`**: Scenarios matching the priority tag are sorted
|
|
282
|
+
first within each worker, but not globally across workers.
|
|
283
|
+
|
|
284
|
+
## License
|
|
285
|
+
|
|
286
|
+
[MIT](LICENSE)
|