pytest-mark-ac 1.0.0__py3-none-any.whl
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.
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Plugin callbacks to register and handle the markers.
|
|
3
|
+
"""
|
|
4
|
+
|
|
5
|
+
from collections.abc import Sequence
|
|
6
|
+
|
|
7
|
+
import pytest
|
|
8
|
+
|
|
9
|
+
_MARK = "ac"
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
def pytest_configure(config: pytest.Config) -> None:
|
|
13
|
+
config.addinivalue_line(
|
|
14
|
+
"markers",
|
|
15
|
+
f"{_MARK}(story_id: int, criteria_ids: Sequence[int], "
|
|
16
|
+
'ref_prefix: str = "ac", ref_suffix: str = ""): '
|
|
17
|
+
"appends suffixes in the form __<ref_prefix><story>_<criterion><ref_suffix> "
|
|
18
|
+
"to the test nodeid",
|
|
19
|
+
)
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
def _validate_and_expand(mark: pytest.Mark) -> list[str]:
|
|
23
|
+
if len(mark.args) != 2:
|
|
24
|
+
raise pytest.UsageError(
|
|
25
|
+
f"@pytest.mark.{_MARK} requires 2 positional arguments: "
|
|
26
|
+
"(story_id:int, criteria_ids:Sequence[int])"
|
|
27
|
+
)
|
|
28
|
+
story_id, criteria_ids = mark.args
|
|
29
|
+
if not isinstance(story_id, int):
|
|
30
|
+
raise pytest.UsageError(f"{_MARK}: story_id must be int, got {type(story_id).__name__}")
|
|
31
|
+
if isinstance(criteria_ids, (str, bytes)) or not isinstance(criteria_ids, Sequence):
|
|
32
|
+
raise pytest.UsageError(f"{_MARK}: criteria_ids must be an int sequence")
|
|
33
|
+
ref_prefix: str = str(mark.kwargs.get("ref_prefix", "ac"))
|
|
34
|
+
ref_suffix: str = str(mark.kwargs.get("ref_suffix", ""))
|
|
35
|
+
out = []
|
|
36
|
+
for idx, cid in enumerate(criteria_ids):
|
|
37
|
+
if not isinstance(cid, int):
|
|
38
|
+
raise pytest.UsageError(
|
|
39
|
+
f"{_MARK}: each criterion must be int, got {type(cid).__name__} at position {idx}"
|
|
40
|
+
)
|
|
41
|
+
out.append(f"__{ref_prefix}{story_id}_{cid}{ref_suffix}")
|
|
42
|
+
return out
|
|
43
|
+
|
|
44
|
+
|
|
45
|
+
def pytest_collection_modifyitems(
|
|
46
|
+
session: pytest.Session, config: pytest.Config, items: list[pytest.Item]
|
|
47
|
+
) -> None:
|
|
48
|
+
for item in items:
|
|
49
|
+
marks = list(item.iter_markers(_MARK))
|
|
50
|
+
if not marks:
|
|
51
|
+
continue
|
|
52
|
+
suffixes: list[str] = []
|
|
53
|
+
for m in marks:
|
|
54
|
+
suffixes.extend(_validate_and_expand(m))
|
|
55
|
+
|
|
56
|
+
seen = set()
|
|
57
|
+
suffixes = [s for s in suffixes if not (s in seen or seen.add(s))]
|
|
58
|
+
suffixes = [s for s in suffixes if s not in item.nodeid]
|
|
59
|
+
if suffixes:
|
|
60
|
+
item._nodeid = item.nodeid + "".join(suffixes)
|
|
@@ -0,0 +1,206 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: pytest-mark-ac
|
|
3
|
+
Version: 1.0.0
|
|
4
|
+
Summary: Provides a marker to reference acceptance criteria from PyTest tests through annotations
|
|
5
|
+
Project-URL: Homepage, https://gitlab.com/Kencho1/pytest-mark-ac
|
|
6
|
+
Project-URL: Documentation, https://gitlab.com/Kencho1/pytest-mark-ac/-/blob/main/README.md
|
|
7
|
+
Project-URL: Repository, https://gitlab.com/Kencho1/pytest-mark-ac.git
|
|
8
|
+
Project-URL: Issues, https://gitlab.com/Kencho1/pytest-mark-ac/-/issues
|
|
9
|
+
Author: Jesús Alonso Abad
|
|
10
|
+
License-Expression: MIT
|
|
11
|
+
License-File: LICENSE
|
|
12
|
+
Classifier: Development Status :: 5 - Production/Stable
|
|
13
|
+
Classifier: Environment :: Plugins
|
|
14
|
+
Classifier: Framework :: Pytest
|
|
15
|
+
Classifier: Intended Audience :: Developers
|
|
16
|
+
Classifier: License :: OSI Approved :: MIT License
|
|
17
|
+
Classifier: Natural Language :: English
|
|
18
|
+
Classifier: Operating System :: OS Independent
|
|
19
|
+
Classifier: Programming Language :: Python :: 3
|
|
20
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
21
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
22
|
+
Classifier: Programming Language :: Python :: 3.13
|
|
23
|
+
Classifier: Programming Language :: Python :: 3.14
|
|
24
|
+
Classifier: Topic :: Software Development :: Libraries :: Python Modules
|
|
25
|
+
Classifier: Topic :: Software Development :: Testing
|
|
26
|
+
Requires-Python: >=3.11
|
|
27
|
+
Requires-Dist: pytest>=9.0.0
|
|
28
|
+
Provides-Extra: dev
|
|
29
|
+
Requires-Dist: ruff>=0.8.0; extra == 'dev'
|
|
30
|
+
Description-Content-Type: text/markdown
|
|
31
|
+
|
|
32
|
+
# pytest-mark-ac
|
|
33
|
+
|
|
34
|
+
A pytest plugin that provides a marker to reference acceptance criteria from tests through annotations. This plugin modifies test node IDs to include acceptance criteria references, enabling traceability between tests and user stories/acceptance criteria.
|
|
35
|
+
|
|
36
|
+
## Installation
|
|
37
|
+
|
|
38
|
+
### From Source
|
|
39
|
+
|
|
40
|
+
```bash
|
|
41
|
+
# Clone the repository
|
|
42
|
+
git clone <repository-url>
|
|
43
|
+
cd pytest-mark-test
|
|
44
|
+
|
|
45
|
+
# Install in development mode
|
|
46
|
+
pip install -e .
|
|
47
|
+
|
|
48
|
+
# Or build and install
|
|
49
|
+
pip install .
|
|
50
|
+
```
|
|
51
|
+
|
|
52
|
+
### From PyPI (when published)
|
|
53
|
+
|
|
54
|
+
```bash
|
|
55
|
+
pip install pytest-mark-ac
|
|
56
|
+
```
|
|
57
|
+
|
|
58
|
+
## Usage
|
|
59
|
+
|
|
60
|
+
The plugin automatically registers when installed. Use the `@pytest.mark.ac` decorator to annotate tests with acceptance criteria references.
|
|
61
|
+
|
|
62
|
+
### Basic Syntax
|
|
63
|
+
|
|
64
|
+
```python
|
|
65
|
+
@pytest.mark.ac(story_id, criteria_ids, ref_prefix="ac", ref_suffix="")
|
|
66
|
+
```
|
|
67
|
+
|
|
68
|
+
**Parameters:**
|
|
69
|
+
- `story_id` (int): The user story ID
|
|
70
|
+
- `criteria_ids` (Sequence[int]): List of acceptance criterion IDs
|
|
71
|
+
- `ref_prefix` (str, optional): Prefix for the reference (default: "ac")
|
|
72
|
+
- `ref_suffix` (str, optional): Suffix for the reference (default: "")
|
|
73
|
+
|
|
74
|
+
**Effect:** Appends `__<ref_prefix><story_id>_<criterion_id><ref_suffix>` to the test node ID
|
|
75
|
+
|
|
76
|
+
## Use Cases and Examples
|
|
77
|
+
|
|
78
|
+
### Use Case 1: Simple Function Test
|
|
79
|
+
|
|
80
|
+
Link a single test to multiple acceptance criteria:
|
|
81
|
+
|
|
82
|
+
```python
|
|
83
|
+
import pytest
|
|
84
|
+
|
|
85
|
+
@pytest.mark.ac(1, [1, 2, 3])
|
|
86
|
+
def test_user_login():
|
|
87
|
+
# Test implementation
|
|
88
|
+
assert True
|
|
89
|
+
```
|
|
90
|
+
|
|
91
|
+
**Result:** Test node ID becomes `test_file.py::test_user_login__ac1_1__ac1_2__ac1_3`
|
|
92
|
+
|
|
93
|
+
Note `range` can be used for sequential ACs as well:
|
|
94
|
+
|
|
95
|
+
```python
|
|
96
|
+
import pytest
|
|
97
|
+
|
|
98
|
+
@pytest.mark.ac(1, *range(1, 4))
|
|
99
|
+
def test_user_login():
|
|
100
|
+
# Test implementation
|
|
101
|
+
assert True
|
|
102
|
+
```
|
|
103
|
+
|
|
104
|
+
also becomes `test_file.py::test_user_login__ac1_1__ac1_2__ac1_3`
|
|
105
|
+
|
|
106
|
+
### Use Case 2: Parametrized Tests with Different Criteria
|
|
107
|
+
|
|
108
|
+
Apply different acceptance criteria to individual parametrized test cases:
|
|
109
|
+
|
|
110
|
+
```python
|
|
111
|
+
from contextlib import nullcontext
|
|
112
|
+
|
|
113
|
+
@pytest.mark.ac(1, [1]) # Applies to all test cases
|
|
114
|
+
@pytest.mark.parametrize(
|
|
115
|
+
'a,b,expected',
|
|
116
|
+
[
|
|
117
|
+
pytest.param(4, 2, nullcontext(2), marks=pytest.mark.ac(2, [1])),
|
|
118
|
+
pytest.param(5, 1, nullcontext(5), marks=pytest.mark.ac(2, [1, 2])),
|
|
119
|
+
pytest.param(3, 2, nullcontext(1.5), marks=pytest.mark.ac(2, [3])),
|
|
120
|
+
pytest.param(3, 0, pytest.raises(ZeroDivisionError), marks=pytest.mark.ac(2, [4])),
|
|
121
|
+
],
|
|
122
|
+
)
|
|
123
|
+
def test_division(a, b, expected):
|
|
124
|
+
with expected as exp_result:
|
|
125
|
+
assert (a / b) == exp_result
|
|
126
|
+
```
|
|
127
|
+
|
|
128
|
+
**Result:**
|
|
129
|
+
- `test_division[4-2-expected0]__ac1_1__ac2_1`
|
|
130
|
+
- `test_division[5-1-expected1]__ac1_1__ac2_1__ac2_2`
|
|
131
|
+
- `test_division[3-2-expected2]__ac1_1__ac2_3`
|
|
132
|
+
- `test_division[3-0-expected3]__ac1_1__ac2_4`
|
|
133
|
+
|
|
134
|
+
### Use Case 3: Custom Reference Format
|
|
135
|
+
|
|
136
|
+
Customize the reference format for your organization's conventions:
|
|
137
|
+
|
|
138
|
+
```python
|
|
139
|
+
@pytest.mark.ac(123, [5, 6], ref_prefix="AC", ref_suffix="_v1")
|
|
140
|
+
def test_feature():
|
|
141
|
+
assert True
|
|
142
|
+
```
|
|
143
|
+
|
|
144
|
+
**Result:** Test node ID becomes `test_file.py::test_feature__AC123_5_v1__AC123_6_v1`
|
|
145
|
+
|
|
146
|
+
### Use Case 4: Testing Edge Cases with Acceptance Criteria
|
|
147
|
+
|
|
148
|
+
```python
|
|
149
|
+
@pytest.mark.parametrize(
|
|
150
|
+
'value,expected',
|
|
151
|
+
[
|
|
152
|
+
pytest.param(0, True, marks=pytest.mark.ac(5, [1])), # Zero case
|
|
153
|
+
pytest.param(-1, True, marks=pytest.mark.ac(5, [2])), # Negative case
|
|
154
|
+
pytest.param(100, False, marks=pytest.mark.ac(5, [3])), # Boundary case
|
|
155
|
+
],
|
|
156
|
+
)
|
|
157
|
+
def test_validation(value, expected):
|
|
158
|
+
assert validate(value) == expected
|
|
159
|
+
```
|
|
160
|
+
|
|
161
|
+
### Use Case 5: Multiple Stories on One Test
|
|
162
|
+
|
|
163
|
+
A test can verify acceptance criteria from multiple user stories:
|
|
164
|
+
|
|
165
|
+
```python
|
|
166
|
+
@pytest.mark.ac(10, [1, 2]) # Story 10, criteria 1-2
|
|
167
|
+
@pytest.mark.ac(11, [3]) # Story 11, criterion 3
|
|
168
|
+
def test_integrated_feature():
|
|
169
|
+
# Tests functionality spanning multiple stories
|
|
170
|
+
assert True
|
|
171
|
+
```
|
|
172
|
+
|
|
173
|
+
**Result:** `test_file.py::test_integrated_feature__ac10_1__ac10_2__ac11_3`
|
|
174
|
+
|
|
175
|
+
## Running Tests with Acceptance Criteria
|
|
176
|
+
|
|
177
|
+
```bash
|
|
178
|
+
# Run all tests (shows modified node IDs)
|
|
179
|
+
pytest -v
|
|
180
|
+
|
|
181
|
+
# Run tests for specific acceptance criterion
|
|
182
|
+
pytest -v -k "ac1_1"
|
|
183
|
+
|
|
184
|
+
# Run tests for specific story
|
|
185
|
+
pytest -v -k "ac5"
|
|
186
|
+
|
|
187
|
+
# Show test collection without running
|
|
188
|
+
pytest --collect-only
|
|
189
|
+
```
|
|
190
|
+
|
|
191
|
+
## Benefits
|
|
192
|
+
|
|
193
|
+
- **Traceability**: Direct link between tests and acceptance criteria
|
|
194
|
+
- **Test Reports**: Node IDs in reports show which criteria are covered
|
|
195
|
+
- **Selective Execution**: Run tests by acceptance criteria using `-k` flag
|
|
196
|
+
- **Documentation**: Tests serve as executable documentation of acceptance criteria
|
|
197
|
+
- **Duplicate Prevention**: Automatically removes duplicate criteria references
|
|
198
|
+
|
|
199
|
+
## Requirements
|
|
200
|
+
|
|
201
|
+
- Python >= 3.11
|
|
202
|
+
- pytest >= 9.0.0
|
|
203
|
+
|
|
204
|
+
## License
|
|
205
|
+
|
|
206
|
+
MIT
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
pytest_mark_ac/__init__.py,sha256=LoJZA4Tn7dzsqMWIqqXwiPVhjx7ACZ-gdypbiBKnPnY,2118
|
|
2
|
+
pytest_mark_ac-1.0.0.dist-info/METADATA,sha256=ABsiOECOl6uOMsaDNRtjzkIPte9Ezpem7U-8MH-BZic,5977
|
|
3
|
+
pytest_mark_ac-1.0.0.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
|
|
4
|
+
pytest_mark_ac-1.0.0.dist-info/entry_points.txt,sha256=tFmAUv6rDus0WNRSfOqyQpgLNnXp1KIFD-Bp2e1SDCk,43
|
|
5
|
+
pytest_mark_ac-1.0.0.dist-info/licenses/LICENSE,sha256=bvnxQii3m3E-t9iSPXaWxmygmXsnPTePBvvalvvwAZA,1075
|
|
6
|
+
pytest_mark_ac-1.0.0.dist-info/RECORD,,
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2025 Jesús Alonso Abad
|
|
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.
|