behave-toolkit 0.1.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_toolkit-0.1.0/LICENSE +21 -0
- behave_toolkit-0.1.0/PKG-INFO +298 -0
- behave_toolkit-0.1.0/README.md +265 -0
- behave_toolkit-0.1.0/pyproject.toml +87 -0
- behave_toolkit-0.1.0/setup.cfg +4 -0
- behave_toolkit-0.1.0/src/behave_toolkit/__init__.py +61 -0
- behave_toolkit-0.1.0/src/behave_toolkit/config.py +606 -0
- behave_toolkit-0.1.0/src/behave_toolkit/cycles.py +209 -0
- behave_toolkit-0.1.0/src/behave_toolkit/errors.py +19 -0
- behave_toolkit-0.1.0/src/behave_toolkit/internal.py +40 -0
- behave_toolkit-0.1.0/src/behave_toolkit/logging_support.py +56 -0
- behave_toolkit-0.1.0/src/behave_toolkit/parsers.py +300 -0
- behave_toolkit-0.1.0/src/behave_toolkit/plugin.py +978 -0
- behave_toolkit-0.1.0/src/behave_toolkit/scopes.py +37 -0
- behave_toolkit-0.1.0/src/behave_toolkit/step_docs.py +1824 -0
- behave_toolkit-0.1.0/src/behave_toolkit.egg-info/PKG-INFO +298 -0
- behave_toolkit-0.1.0/src/behave_toolkit.egg-info/SOURCES.txt +27 -0
- behave_toolkit-0.1.0/src/behave_toolkit.egg-info/dependency_links.txt +1 -0
- behave_toolkit-0.1.0/src/behave_toolkit.egg-info/entry_points.txt +2 -0
- behave_toolkit-0.1.0/src/behave_toolkit.egg-info/requires.txt +13 -0
- behave_toolkit-0.1.0/src/behave_toolkit.egg-info/top_level.txt +1 -0
- behave_toolkit-0.1.0/tests/test_config.py +228 -0
- behave_toolkit-0.1.0/tests/test_cycles.py +163 -0
- behave_toolkit-0.1.0/tests/test_lifecycle.py +364 -0
- behave_toolkit-0.1.0/tests/test_logging.py +73 -0
- behave_toolkit-0.1.0/tests/test_logging_config.py +57 -0
- behave_toolkit-0.1.0/tests/test_parsers.py +310 -0
- behave_toolkit-0.1.0/tests/test_plugin.py +129 -0
- behave_toolkit-0.1.0/tests/test_step_docs.py +242 -0
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Denis
|
|
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,298 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: behave-toolkit
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: Toolkit helpers for Behave test suites.
|
|
5
|
+
Author: Denis
|
|
6
|
+
License-Expression: MIT
|
|
7
|
+
Project-URL: Homepage, https://github.com/joshlarssen/behave-toolkit
|
|
8
|
+
Project-URL: Repository, https://github.com/joshlarssen/behave-toolkit
|
|
9
|
+
Project-URL: Issues, https://github.com/joshlarssen/behave-toolkit/issues
|
|
10
|
+
Project-URL: Documentation, https://joshlarssen.github.io/behave-toolkit/
|
|
11
|
+
Project-URL: Changelog, https://github.com/joshlarssen/behave-toolkit/blob/main/CHANGELOG.md
|
|
12
|
+
Keywords: behave,bdd,testing,qa,yaml
|
|
13
|
+
Classifier: Operating System :: OS Independent
|
|
14
|
+
Classifier: Programming Language :: Python :: 3
|
|
15
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
16
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
17
|
+
Classifier: Topic :: Software Development :: Testing
|
|
18
|
+
Requires-Python: >=3.11
|
|
19
|
+
Description-Content-Type: text/markdown
|
|
20
|
+
License-File: LICENSE
|
|
21
|
+
Requires-Dist: PyYAML>=6.0
|
|
22
|
+
Requires-Dist: behave>=1.3.3
|
|
23
|
+
Requires-Dist: Sphinx>=8.2
|
|
24
|
+
Requires-Dist: myst-parser>=4.0
|
|
25
|
+
Requires-Dist: furo>=2024.8.6
|
|
26
|
+
Requires-Dist: sphinx-design>=0.6
|
|
27
|
+
Provides-Extra: dev
|
|
28
|
+
Requires-Dist: mypy>=1.10; extra == "dev"
|
|
29
|
+
Requires-Dist: pylint>=3.2; extra == "dev"
|
|
30
|
+
Requires-Dist: types-PyYAML>=6.0.12.20250326; extra == "dev"
|
|
31
|
+
Provides-Extra: docs
|
|
32
|
+
Dynamic: license-file
|
|
33
|
+
|
|
34
|
+
# behave-toolkit
|
|
35
|
+
|
|
36
|
+
`behave-toolkit` is a small toolkit for teams that like Behave's explicit
|
|
37
|
+
execution model but want less repetitive wiring as suites grow.
|
|
38
|
+
|
|
39
|
+
The project is deliberately pragmatic:
|
|
40
|
+
|
|
41
|
+
- keep `features/environment.py` readable
|
|
42
|
+
- make object lifetimes explicit with `global`, `feature`, and `scenario`
|
|
43
|
+
scopes
|
|
44
|
+
- move repetitive setup into explicit YAML instead of hidden framework magic
|
|
45
|
+
- add a few focused helpers only where Behave gets noisy in larger suites
|
|
46
|
+
|
|
47
|
+
## What it gives you today
|
|
48
|
+
|
|
49
|
+
- YAML-configured objects with deterministic file or directory loading
|
|
50
|
+
- fail-fast validation with dedicated `ConfigError` and `IntegrationError`
|
|
51
|
+
exceptions
|
|
52
|
+
- explicit `$ref` and `$var` markers for dependencies and reusable values
|
|
53
|
+
- lifecycle activation helpers for `environment.py`
|
|
54
|
+
- config-driven parser helpers for Behave custom types
|
|
55
|
+
- tag-driven scenario cycling with `@cycling(N)`
|
|
56
|
+
- generated step reference documentation for consumer Behave suites
|
|
57
|
+
- one small persistent test logger helper, plus optional YAML-defined named
|
|
58
|
+
loggers if you later need more than one output
|
|
59
|
+
|
|
60
|
+
Project documentation for `behave-toolkit` itself lives in `docs/` and is meant
|
|
61
|
+
to be published on GitHub Pages.
|
|
62
|
+
|
|
63
|
+
## Quick start
|
|
64
|
+
|
|
65
|
+
Install the package:
|
|
66
|
+
|
|
67
|
+
```bash
|
|
68
|
+
pip install behave-toolkit
|
|
69
|
+
```
|
|
70
|
+
|
|
71
|
+
That single install now gives you both main usage modes:
|
|
72
|
+
|
|
73
|
+
- the Python integration API used from `features/environment.py`
|
|
74
|
+
- the `behave-toolkit-docs` CLI plus the Sphinx toolchain needed to build HTML
|
|
75
|
+
|
|
76
|
+
Create a configuration file:
|
|
77
|
+
|
|
78
|
+
```yaml
|
|
79
|
+
version: 1
|
|
80
|
+
variables:
|
|
81
|
+
report_name: report.json
|
|
82
|
+
|
|
83
|
+
objects:
|
|
84
|
+
workspace:
|
|
85
|
+
factory: tempfile.TemporaryDirectory
|
|
86
|
+
scope: feature
|
|
87
|
+
cleanup: cleanup
|
|
88
|
+
|
|
89
|
+
workspace_path:
|
|
90
|
+
factory: pathlib.Path
|
|
91
|
+
scope: feature
|
|
92
|
+
args:
|
|
93
|
+
- $ref: workspace
|
|
94
|
+
attr: name
|
|
95
|
+
|
|
96
|
+
report_path:
|
|
97
|
+
factory: pathlib.Path
|
|
98
|
+
scope: scenario
|
|
99
|
+
args:
|
|
100
|
+
- $ref: workspace_path
|
|
101
|
+
- $var: report_name
|
|
102
|
+
```
|
|
103
|
+
|
|
104
|
+
Wire it from `features/environment.py`:
|
|
105
|
+
|
|
106
|
+
```python
|
|
107
|
+
from pathlib import Path
|
|
108
|
+
|
|
109
|
+
from behave_toolkit import (
|
|
110
|
+
activate_feature_scope,
|
|
111
|
+
activate_scenario_scope,
|
|
112
|
+
install,
|
|
113
|
+
)
|
|
114
|
+
|
|
115
|
+
CONFIG_PATH = Path(__file__).with_name("behave-toolkit.yaml")
|
|
116
|
+
|
|
117
|
+
|
|
118
|
+
def before_all(context):
|
|
119
|
+
install(context, CONFIG_PATH)
|
|
120
|
+
|
|
121
|
+
|
|
122
|
+
def before_feature(context, feature):
|
|
123
|
+
del feature
|
|
124
|
+
activate_feature_scope(context)
|
|
125
|
+
|
|
126
|
+
|
|
127
|
+
def before_scenario(context, scenario):
|
|
128
|
+
del scenario
|
|
129
|
+
activate_scenario_scope(context)
|
|
130
|
+
```
|
|
131
|
+
|
|
132
|
+
For many suites, that is enough. `install()` creates `global` objects during
|
|
133
|
+
`before_all()`, `activate_feature_scope()` creates feature objects,
|
|
134
|
+
`activate_scenario_scope()` creates scenario objects, and instances are exposed
|
|
135
|
+
on the Behave context using either `inject_as` or the object name.
|
|
136
|
+
|
|
137
|
+
That means the default `global` lifecycle is:
|
|
138
|
+
|
|
139
|
+
- created from `before_all()` when you call `install(context, CONFIG_PATH)`
|
|
140
|
+
- kept alive for the whole Behave run
|
|
141
|
+
- cleaned automatically when Behave tears down the test-run layer at the very end
|
|
142
|
+
|
|
143
|
+
`factory` can point to:
|
|
144
|
+
|
|
145
|
+
- your own project code
|
|
146
|
+
- an installed package from the active environment
|
|
147
|
+
- the Python standard library
|
|
148
|
+
|
|
149
|
+
Markers are explicit on purpose:
|
|
150
|
+
|
|
151
|
+
- `$ref`: inject another configured object
|
|
152
|
+
- `$ref` + `attr`: inject one attribute path from another object
|
|
153
|
+
- `$var`: inject a named value from the root `variables` section
|
|
154
|
+
|
|
155
|
+
`install()` validates the whole configuration up front. Invalid scopes, bad
|
|
156
|
+
imports, unknown `$ref` / `$var` entries, and object-reference cycles fail fast
|
|
157
|
+
with messages that include the config path and the relevant object field.
|
|
158
|
+
|
|
159
|
+
When the config grows, `CONFIG_PATH` can point to a dedicated config directory
|
|
160
|
+
instead of one file. `behave-toolkit` loads all `.yaml` / `.yml` files from that
|
|
161
|
+
directory recursively in deterministic order and merges them. Duplicated names
|
|
162
|
+
across files fail fast so ownership stays explicit.
|
|
163
|
+
|
|
164
|
+
Example layout:
|
|
165
|
+
|
|
166
|
+
```text
|
|
167
|
+
features/
|
|
168
|
+
behave-toolkit/
|
|
169
|
+
00-variables.yaml
|
|
170
|
+
10-parsers.yaml
|
|
171
|
+
20-objects.yaml
|
|
172
|
+
30-logging.yaml
|
|
173
|
+
```
|
|
174
|
+
|
|
175
|
+
## Add optional helpers only when you need them
|
|
176
|
+
|
|
177
|
+
### Parser helpers
|
|
178
|
+
|
|
179
|
+
If your suite uses custom Behave types, `configure_parsers(CONFIG_PATH)` can
|
|
180
|
+
move matcher selection and type registration into the same YAML config. This is
|
|
181
|
+
import-time setup, so keep it at module level in `environment.py`.
|
|
182
|
+
|
|
183
|
+
### Scenario cycling
|
|
184
|
+
|
|
185
|
+
If you want to replay one plain scenario several times, call
|
|
186
|
+
`expand_scenario_cycles(context)` from `before_all()` and tag the scenario with
|
|
187
|
+
`@cycling(N)`. Each replay keeps its own scenario hooks, context layer, and
|
|
188
|
+
report entry.
|
|
189
|
+
|
|
190
|
+
### Logging
|
|
191
|
+
|
|
192
|
+
If your config exposes a path object such as `test_log_path`, the recommended
|
|
193
|
+
default is one persistent test log:
|
|
194
|
+
|
|
195
|
+
```python
|
|
196
|
+
from behave_toolkit import configure_test_logging
|
|
197
|
+
|
|
198
|
+
|
|
199
|
+
def before_all(context):
|
|
200
|
+
install(context, CONFIG_PATH)
|
|
201
|
+
context.test_logger = configure_test_logging(context.test_log_path)
|
|
202
|
+
```
|
|
203
|
+
|
|
204
|
+
If that one file is enough, stop there. If you later need several named outputs,
|
|
205
|
+
the optional `logging:` section plus `configure_loggers(context)` can
|
|
206
|
+
materialize them from YAML.
|
|
207
|
+
|
|
208
|
+
### Step documentation
|
|
209
|
+
|
|
210
|
+
To generate a technical reference site from a consumer Behave project:
|
|
211
|
+
|
|
212
|
+
```bash
|
|
213
|
+
behave-toolkit-docs --features-dir features --output-dir docs/behave-toolkit
|
|
214
|
+
python -m sphinx -b html docs/behave-toolkit docs/_build/behave-toolkit
|
|
215
|
+
```
|
|
216
|
+
|
|
217
|
+
The generated pages include grouped step catalogs, one page per step
|
|
218
|
+
definition, one page per custom parse type, and links from typed parameters
|
|
219
|
+
back to their type pages.
|
|
220
|
+
|
|
221
|
+
## Project documentation
|
|
222
|
+
|
|
223
|
+
Build the main project documentation locally with:
|
|
224
|
+
|
|
225
|
+
```bash
|
|
226
|
+
pip install -e .
|
|
227
|
+
python -m sphinx -W --keep-going -b html docs docs/_build/html
|
|
228
|
+
```
|
|
229
|
+
|
|
230
|
+
A dedicated GitHub Actions workflow builds this site on pull requests and
|
|
231
|
+
deploys it to GitHub Pages from `main`.
|
|
232
|
+
|
|
233
|
+
The workflow always validates the docs build. Deployment starts automatically
|
|
234
|
+
after a one-time GitHub setup in `Settings > Pages`: set
|
|
235
|
+
`Build and deployment > Source` to `GitHub Actions`.
|
|
236
|
+
|
|
237
|
+
## Releasing
|
|
238
|
+
|
|
239
|
+
Releases are automated with `.github/workflows/release.yml`.
|
|
240
|
+
|
|
241
|
+
- release preparation starts from a short-lived branch named `release/X.Y.Z`
|
|
242
|
+
- pushing that branch prepares or updates a release PR that targets `main`
|
|
243
|
+
- the release PR updates `CHANGELOG.md` and the package version metadata
|
|
244
|
+
- after the PR lands on `main`, the workflow finalizes the tag, GitHub release,
|
|
245
|
+
package artifacts, and PyPI publication
|
|
246
|
+
- the workflow stays dormant until you set the repository variable
|
|
247
|
+
`ENABLE_RELEASE_PLEASE=true`
|
|
248
|
+
|
|
249
|
+
For the first public release, create `release/0.1.0`, let the workflow open the
|
|
250
|
+
release PR, then review that generated `CHANGELOG.md` entry manually before
|
|
251
|
+
merging so the first public notes read like a curated initial release rather
|
|
252
|
+
than raw bootstrap history.
|
|
253
|
+
|
|
254
|
+
If you also set `ENABLE_RELEASE_AUTOMERGE=true`, the workflow will enable
|
|
255
|
+
auto-merge for release PRs after `0.1.0`. Keep the first release manual.
|
|
256
|
+
|
|
257
|
+
Repository setup for the release workflow:
|
|
258
|
+
|
|
259
|
+
- set the repository variable `ENABLE_RELEASE_PLEASE=true` only after the
|
|
260
|
+
release workflow is actually configured
|
|
261
|
+
- optionally set `ENABLE_RELEASE_AUTOMERGE=true` after enabling repository
|
|
262
|
+
auto-merge and deciding that non-initial releases may merge automatically
|
|
263
|
+
- enable `Settings > Actions > General > Allow GitHub Actions to create and approve pull requests`
|
|
264
|
+
- enable repository auto-merge if you want later release PRs to merge by
|
|
265
|
+
themselves once checks pass
|
|
266
|
+
- configure a PyPI Trusted Publisher for the `Release` workflow before the first publish
|
|
267
|
+
- optionally add a `RELEASE_PLEASE_TOKEN` secret if you also want CI workflows to run on Release Please PRs
|
|
268
|
+
|
|
269
|
+
## Development
|
|
270
|
+
|
|
271
|
+
Install the package in editable mode with development tools:
|
|
272
|
+
|
|
273
|
+
```bash
|
|
274
|
+
pip install -e ".[dev]"
|
|
275
|
+
```
|
|
276
|
+
|
|
277
|
+
Run the test suite:
|
|
278
|
+
|
|
279
|
+
```bash
|
|
280
|
+
python -m unittest discover -s tests -v
|
|
281
|
+
```
|
|
282
|
+
|
|
283
|
+
Run a quick syntax validation:
|
|
284
|
+
|
|
285
|
+
```bash
|
|
286
|
+
python -m compileall src tests test_support.py
|
|
287
|
+
```
|
|
288
|
+
|
|
289
|
+
Run static analysis:
|
|
290
|
+
|
|
291
|
+
```bash
|
|
292
|
+
python -m mypy src tests test_support.py
|
|
293
|
+
python -m pylint src tests test_support.py
|
|
294
|
+
```
|
|
295
|
+
|
|
296
|
+
The GitHub Actions CI workflow runs static analysis once on Ubuntu with Python
|
|
297
|
+
3.11, then runs the unit tests in a smaller Ubuntu and Windows matrix for
|
|
298
|
+
Python 3.11 and 3.12.
|
|
@@ -0,0 +1,265 @@
|
|
|
1
|
+
# behave-toolkit
|
|
2
|
+
|
|
3
|
+
`behave-toolkit` is a small toolkit for teams that like Behave's explicit
|
|
4
|
+
execution model but want less repetitive wiring as suites grow.
|
|
5
|
+
|
|
6
|
+
The project is deliberately pragmatic:
|
|
7
|
+
|
|
8
|
+
- keep `features/environment.py` readable
|
|
9
|
+
- make object lifetimes explicit with `global`, `feature`, and `scenario`
|
|
10
|
+
scopes
|
|
11
|
+
- move repetitive setup into explicit YAML instead of hidden framework magic
|
|
12
|
+
- add a few focused helpers only where Behave gets noisy in larger suites
|
|
13
|
+
|
|
14
|
+
## What it gives you today
|
|
15
|
+
|
|
16
|
+
- YAML-configured objects with deterministic file or directory loading
|
|
17
|
+
- fail-fast validation with dedicated `ConfigError` and `IntegrationError`
|
|
18
|
+
exceptions
|
|
19
|
+
- explicit `$ref` and `$var` markers for dependencies and reusable values
|
|
20
|
+
- lifecycle activation helpers for `environment.py`
|
|
21
|
+
- config-driven parser helpers for Behave custom types
|
|
22
|
+
- tag-driven scenario cycling with `@cycling(N)`
|
|
23
|
+
- generated step reference documentation for consumer Behave suites
|
|
24
|
+
- one small persistent test logger helper, plus optional YAML-defined named
|
|
25
|
+
loggers if you later need more than one output
|
|
26
|
+
|
|
27
|
+
Project documentation for `behave-toolkit` itself lives in `docs/` and is meant
|
|
28
|
+
to be published on GitHub Pages.
|
|
29
|
+
|
|
30
|
+
## Quick start
|
|
31
|
+
|
|
32
|
+
Install the package:
|
|
33
|
+
|
|
34
|
+
```bash
|
|
35
|
+
pip install behave-toolkit
|
|
36
|
+
```
|
|
37
|
+
|
|
38
|
+
That single install now gives you both main usage modes:
|
|
39
|
+
|
|
40
|
+
- the Python integration API used from `features/environment.py`
|
|
41
|
+
- the `behave-toolkit-docs` CLI plus the Sphinx toolchain needed to build HTML
|
|
42
|
+
|
|
43
|
+
Create a configuration file:
|
|
44
|
+
|
|
45
|
+
```yaml
|
|
46
|
+
version: 1
|
|
47
|
+
variables:
|
|
48
|
+
report_name: report.json
|
|
49
|
+
|
|
50
|
+
objects:
|
|
51
|
+
workspace:
|
|
52
|
+
factory: tempfile.TemporaryDirectory
|
|
53
|
+
scope: feature
|
|
54
|
+
cleanup: cleanup
|
|
55
|
+
|
|
56
|
+
workspace_path:
|
|
57
|
+
factory: pathlib.Path
|
|
58
|
+
scope: feature
|
|
59
|
+
args:
|
|
60
|
+
- $ref: workspace
|
|
61
|
+
attr: name
|
|
62
|
+
|
|
63
|
+
report_path:
|
|
64
|
+
factory: pathlib.Path
|
|
65
|
+
scope: scenario
|
|
66
|
+
args:
|
|
67
|
+
- $ref: workspace_path
|
|
68
|
+
- $var: report_name
|
|
69
|
+
```
|
|
70
|
+
|
|
71
|
+
Wire it from `features/environment.py`:
|
|
72
|
+
|
|
73
|
+
```python
|
|
74
|
+
from pathlib import Path
|
|
75
|
+
|
|
76
|
+
from behave_toolkit import (
|
|
77
|
+
activate_feature_scope,
|
|
78
|
+
activate_scenario_scope,
|
|
79
|
+
install,
|
|
80
|
+
)
|
|
81
|
+
|
|
82
|
+
CONFIG_PATH = Path(__file__).with_name("behave-toolkit.yaml")
|
|
83
|
+
|
|
84
|
+
|
|
85
|
+
def before_all(context):
|
|
86
|
+
install(context, CONFIG_PATH)
|
|
87
|
+
|
|
88
|
+
|
|
89
|
+
def before_feature(context, feature):
|
|
90
|
+
del feature
|
|
91
|
+
activate_feature_scope(context)
|
|
92
|
+
|
|
93
|
+
|
|
94
|
+
def before_scenario(context, scenario):
|
|
95
|
+
del scenario
|
|
96
|
+
activate_scenario_scope(context)
|
|
97
|
+
```
|
|
98
|
+
|
|
99
|
+
For many suites, that is enough. `install()` creates `global` objects during
|
|
100
|
+
`before_all()`, `activate_feature_scope()` creates feature objects,
|
|
101
|
+
`activate_scenario_scope()` creates scenario objects, and instances are exposed
|
|
102
|
+
on the Behave context using either `inject_as` or the object name.
|
|
103
|
+
|
|
104
|
+
That means the default `global` lifecycle is:
|
|
105
|
+
|
|
106
|
+
- created from `before_all()` when you call `install(context, CONFIG_PATH)`
|
|
107
|
+
- kept alive for the whole Behave run
|
|
108
|
+
- cleaned automatically when Behave tears down the test-run layer at the very end
|
|
109
|
+
|
|
110
|
+
`factory` can point to:
|
|
111
|
+
|
|
112
|
+
- your own project code
|
|
113
|
+
- an installed package from the active environment
|
|
114
|
+
- the Python standard library
|
|
115
|
+
|
|
116
|
+
Markers are explicit on purpose:
|
|
117
|
+
|
|
118
|
+
- `$ref`: inject another configured object
|
|
119
|
+
- `$ref` + `attr`: inject one attribute path from another object
|
|
120
|
+
- `$var`: inject a named value from the root `variables` section
|
|
121
|
+
|
|
122
|
+
`install()` validates the whole configuration up front. Invalid scopes, bad
|
|
123
|
+
imports, unknown `$ref` / `$var` entries, and object-reference cycles fail fast
|
|
124
|
+
with messages that include the config path and the relevant object field.
|
|
125
|
+
|
|
126
|
+
When the config grows, `CONFIG_PATH` can point to a dedicated config directory
|
|
127
|
+
instead of one file. `behave-toolkit` loads all `.yaml` / `.yml` files from that
|
|
128
|
+
directory recursively in deterministic order and merges them. Duplicated names
|
|
129
|
+
across files fail fast so ownership stays explicit.
|
|
130
|
+
|
|
131
|
+
Example layout:
|
|
132
|
+
|
|
133
|
+
```text
|
|
134
|
+
features/
|
|
135
|
+
behave-toolkit/
|
|
136
|
+
00-variables.yaml
|
|
137
|
+
10-parsers.yaml
|
|
138
|
+
20-objects.yaml
|
|
139
|
+
30-logging.yaml
|
|
140
|
+
```
|
|
141
|
+
|
|
142
|
+
## Add optional helpers only when you need them
|
|
143
|
+
|
|
144
|
+
### Parser helpers
|
|
145
|
+
|
|
146
|
+
If your suite uses custom Behave types, `configure_parsers(CONFIG_PATH)` can
|
|
147
|
+
move matcher selection and type registration into the same YAML config. This is
|
|
148
|
+
import-time setup, so keep it at module level in `environment.py`.
|
|
149
|
+
|
|
150
|
+
### Scenario cycling
|
|
151
|
+
|
|
152
|
+
If you want to replay one plain scenario several times, call
|
|
153
|
+
`expand_scenario_cycles(context)` from `before_all()` and tag the scenario with
|
|
154
|
+
`@cycling(N)`. Each replay keeps its own scenario hooks, context layer, and
|
|
155
|
+
report entry.
|
|
156
|
+
|
|
157
|
+
### Logging
|
|
158
|
+
|
|
159
|
+
If your config exposes a path object such as `test_log_path`, the recommended
|
|
160
|
+
default is one persistent test log:
|
|
161
|
+
|
|
162
|
+
```python
|
|
163
|
+
from behave_toolkit import configure_test_logging
|
|
164
|
+
|
|
165
|
+
|
|
166
|
+
def before_all(context):
|
|
167
|
+
install(context, CONFIG_PATH)
|
|
168
|
+
context.test_logger = configure_test_logging(context.test_log_path)
|
|
169
|
+
```
|
|
170
|
+
|
|
171
|
+
If that one file is enough, stop there. If you later need several named outputs,
|
|
172
|
+
the optional `logging:` section plus `configure_loggers(context)` can
|
|
173
|
+
materialize them from YAML.
|
|
174
|
+
|
|
175
|
+
### Step documentation
|
|
176
|
+
|
|
177
|
+
To generate a technical reference site from a consumer Behave project:
|
|
178
|
+
|
|
179
|
+
```bash
|
|
180
|
+
behave-toolkit-docs --features-dir features --output-dir docs/behave-toolkit
|
|
181
|
+
python -m sphinx -b html docs/behave-toolkit docs/_build/behave-toolkit
|
|
182
|
+
```
|
|
183
|
+
|
|
184
|
+
The generated pages include grouped step catalogs, one page per step
|
|
185
|
+
definition, one page per custom parse type, and links from typed parameters
|
|
186
|
+
back to their type pages.
|
|
187
|
+
|
|
188
|
+
## Project documentation
|
|
189
|
+
|
|
190
|
+
Build the main project documentation locally with:
|
|
191
|
+
|
|
192
|
+
```bash
|
|
193
|
+
pip install -e .
|
|
194
|
+
python -m sphinx -W --keep-going -b html docs docs/_build/html
|
|
195
|
+
```
|
|
196
|
+
|
|
197
|
+
A dedicated GitHub Actions workflow builds this site on pull requests and
|
|
198
|
+
deploys it to GitHub Pages from `main`.
|
|
199
|
+
|
|
200
|
+
The workflow always validates the docs build. Deployment starts automatically
|
|
201
|
+
after a one-time GitHub setup in `Settings > Pages`: set
|
|
202
|
+
`Build and deployment > Source` to `GitHub Actions`.
|
|
203
|
+
|
|
204
|
+
## Releasing
|
|
205
|
+
|
|
206
|
+
Releases are automated with `.github/workflows/release.yml`.
|
|
207
|
+
|
|
208
|
+
- release preparation starts from a short-lived branch named `release/X.Y.Z`
|
|
209
|
+
- pushing that branch prepares or updates a release PR that targets `main`
|
|
210
|
+
- the release PR updates `CHANGELOG.md` and the package version metadata
|
|
211
|
+
- after the PR lands on `main`, the workflow finalizes the tag, GitHub release,
|
|
212
|
+
package artifacts, and PyPI publication
|
|
213
|
+
- the workflow stays dormant until you set the repository variable
|
|
214
|
+
`ENABLE_RELEASE_PLEASE=true`
|
|
215
|
+
|
|
216
|
+
For the first public release, create `release/0.1.0`, let the workflow open the
|
|
217
|
+
release PR, then review that generated `CHANGELOG.md` entry manually before
|
|
218
|
+
merging so the first public notes read like a curated initial release rather
|
|
219
|
+
than raw bootstrap history.
|
|
220
|
+
|
|
221
|
+
If you also set `ENABLE_RELEASE_AUTOMERGE=true`, the workflow will enable
|
|
222
|
+
auto-merge for release PRs after `0.1.0`. Keep the first release manual.
|
|
223
|
+
|
|
224
|
+
Repository setup for the release workflow:
|
|
225
|
+
|
|
226
|
+
- set the repository variable `ENABLE_RELEASE_PLEASE=true` only after the
|
|
227
|
+
release workflow is actually configured
|
|
228
|
+
- optionally set `ENABLE_RELEASE_AUTOMERGE=true` after enabling repository
|
|
229
|
+
auto-merge and deciding that non-initial releases may merge automatically
|
|
230
|
+
- enable `Settings > Actions > General > Allow GitHub Actions to create and approve pull requests`
|
|
231
|
+
- enable repository auto-merge if you want later release PRs to merge by
|
|
232
|
+
themselves once checks pass
|
|
233
|
+
- configure a PyPI Trusted Publisher for the `Release` workflow before the first publish
|
|
234
|
+
- optionally add a `RELEASE_PLEASE_TOKEN` secret if you also want CI workflows to run on Release Please PRs
|
|
235
|
+
|
|
236
|
+
## Development
|
|
237
|
+
|
|
238
|
+
Install the package in editable mode with development tools:
|
|
239
|
+
|
|
240
|
+
```bash
|
|
241
|
+
pip install -e ".[dev]"
|
|
242
|
+
```
|
|
243
|
+
|
|
244
|
+
Run the test suite:
|
|
245
|
+
|
|
246
|
+
```bash
|
|
247
|
+
python -m unittest discover -s tests -v
|
|
248
|
+
```
|
|
249
|
+
|
|
250
|
+
Run a quick syntax validation:
|
|
251
|
+
|
|
252
|
+
```bash
|
|
253
|
+
python -m compileall src tests test_support.py
|
|
254
|
+
```
|
|
255
|
+
|
|
256
|
+
Run static analysis:
|
|
257
|
+
|
|
258
|
+
```bash
|
|
259
|
+
python -m mypy src tests test_support.py
|
|
260
|
+
python -m pylint src tests test_support.py
|
|
261
|
+
```
|
|
262
|
+
|
|
263
|
+
The GitHub Actions CI workflow runs static analysis once on Ubuntu with Python
|
|
264
|
+
3.11, then runs the unit tests in a smaller Ubuntu and Windows matrix for
|
|
265
|
+
Python 3.11 and 3.12.
|
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
[build-system]
|
|
2
|
+
requires = ["setuptools>=77", "wheel"]
|
|
3
|
+
build-backend = "setuptools.build_meta"
|
|
4
|
+
|
|
5
|
+
[project]
|
|
6
|
+
name = "behave-toolkit"
|
|
7
|
+
version = "0.1.0"
|
|
8
|
+
description = "Toolkit helpers for Behave test suites."
|
|
9
|
+
readme = "README.md"
|
|
10
|
+
requires-python = ">=3.11"
|
|
11
|
+
license = "MIT"
|
|
12
|
+
license-files = ["LICENSE"]
|
|
13
|
+
dependencies = [
|
|
14
|
+
"PyYAML>=6.0",
|
|
15
|
+
"behave>=1.3.3",
|
|
16
|
+
"Sphinx>=8.2",
|
|
17
|
+
"myst-parser>=4.0",
|
|
18
|
+
"furo>=2024.8.6",
|
|
19
|
+
"sphinx-design>=0.6",
|
|
20
|
+
]
|
|
21
|
+
authors = [
|
|
22
|
+
{ name = "Denis" }
|
|
23
|
+
]
|
|
24
|
+
keywords = ["behave", "bdd", "testing", "qa", "yaml"]
|
|
25
|
+
classifiers = [
|
|
26
|
+
"Operating System :: OS Independent",
|
|
27
|
+
"Programming Language :: Python :: 3",
|
|
28
|
+
"Programming Language :: Python :: 3.11",
|
|
29
|
+
"Programming Language :: Python :: 3.12",
|
|
30
|
+
"Topic :: Software Development :: Testing",
|
|
31
|
+
]
|
|
32
|
+
|
|
33
|
+
[project.scripts]
|
|
34
|
+
behave-toolkit-docs = "behave_toolkit.step_docs:main"
|
|
35
|
+
|
|
36
|
+
[project.optional-dependencies]
|
|
37
|
+
dev = [
|
|
38
|
+
"mypy>=1.10",
|
|
39
|
+
"pylint>=3.2",
|
|
40
|
+
"types-PyYAML>=6.0.12.20250326",
|
|
41
|
+
]
|
|
42
|
+
docs = []
|
|
43
|
+
|
|
44
|
+
[project.urls]
|
|
45
|
+
Homepage = "https://github.com/joshlarssen/behave-toolkit"
|
|
46
|
+
Repository = "https://github.com/joshlarssen/behave-toolkit"
|
|
47
|
+
Issues = "https://github.com/joshlarssen/behave-toolkit/issues"
|
|
48
|
+
Documentation = "https://joshlarssen.github.io/behave-toolkit/"
|
|
49
|
+
Changelog = "https://github.com/joshlarssen/behave-toolkit/blob/main/CHANGELOG.md"
|
|
50
|
+
|
|
51
|
+
[tool.setuptools]
|
|
52
|
+
package-dir = {"" = "src"}
|
|
53
|
+
|
|
54
|
+
[tool.setuptools.packages.find]
|
|
55
|
+
where = ["src"]
|
|
56
|
+
|
|
57
|
+
[tool.mypy]
|
|
58
|
+
python_version = "3.11"
|
|
59
|
+
mypy_path = "$MYPY_CONFIG_FILE_DIR/src"
|
|
60
|
+
files = ["src", "tests", "test_support.py"]
|
|
61
|
+
check_untyped_defs = true
|
|
62
|
+
disallow_untyped_defs = true
|
|
63
|
+
no_implicit_optional = true
|
|
64
|
+
warn_redundant_casts = true
|
|
65
|
+
warn_return_any = true
|
|
66
|
+
warn_unused_configs = true
|
|
67
|
+
warn_unused_ignores = true
|
|
68
|
+
|
|
69
|
+
[[tool.mypy.overrides]]
|
|
70
|
+
module = ["behave", "behave.*"]
|
|
71
|
+
ignore_missing_imports = true
|
|
72
|
+
|
|
73
|
+
[tool.pylint.main]
|
|
74
|
+
py-version = "3.11"
|
|
75
|
+
jobs = 0
|
|
76
|
+
init-hook = "import sys; from pathlib import Path; sys.path.insert(0, str(Path().resolve() / 'src'))"
|
|
77
|
+
|
|
78
|
+
[tool.pylint.messages_control]
|
|
79
|
+
disable = [
|
|
80
|
+
"missing-class-docstring",
|
|
81
|
+
"missing-function-docstring",
|
|
82
|
+
"missing-module-docstring",
|
|
83
|
+
"too-few-public-methods",
|
|
84
|
+
]
|
|
85
|
+
|
|
86
|
+
[tool.pylint.format]
|
|
87
|
+
max-line-length = 100
|