pytest-scenario-files 0.12__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.
- pytest_scenario_files-0.12/.gitignore +76 -0
- pytest_scenario_files-0.12/.pre-commit-config.yaml +29 -0
- pytest_scenario_files-0.12/CHANGES.md +64 -0
- pytest_scenario_files-0.12/LICENSE +22 -0
- pytest_scenario_files-0.12/PKG-INFO +373 -0
- pytest_scenario_files-0.12/README.md +339 -0
- pytest_scenario_files-0.12/_version.py +16 -0
- pytest_scenario_files-0.12/pyproject.toml +240 -0
- pytest_scenario_files-0.12/src/pytest_scenario_files/__init__.py +2 -0
- pytest_scenario_files-0.12/src/pytest_scenario_files/plugin.py +241 -0
- pytest_scenario_files-0.12/tests/conftest.py +1 -0
- pytest_scenario_files-0.12/tests/failure cases/test_data_file_not_dict.py +10 -0
- pytest_scenario_files-0.12/tests/failure cases/test_data_file_wrong_ext.py +10 -0
- pytest_scenario_files-0.12/tests/failure cases/test_fixture_merge_conflict.py +11 -0
- pytest_scenario_files-0.12/tests/failure cases/test_fixture_mismatch.py +11 -0
- pytest_scenario_files-0.12/tests/failure cases/test_no_data_files_found.py +9 -0
- pytest_scenario_files-0.12/tests/failure cases/test_test_case_not_dict.py +10 -0
- pytest_scenario_files-0.12/tests/pytester_example_files/data_data_file_not_dict_tester.yaml +1 -0
- pytest_scenario_files-0.12/tests/pytester_example_files/data_data_file_wrong_ext_tester.txt +9 -0
- pytest_scenario_files-0.12/tests/pytester_example_files/data_fixture_merge_conflict_tester_1.yaml +5 -0
- pytest_scenario_files-0.12/tests/pytester_example_files/data_fixture_merge_conflict_tester_2.yaml +18 -0
- pytest_scenario_files-0.12/tests/pytester_example_files/data_fixture_mismatch_tester_1.yaml +7 -0
- pytest_scenario_files-0.12/tests/pytester_example_files/data_fixture_mismatch_tester_2.yaml +4 -0
- pytest_scenario_files-0.12/tests/pytester_example_files/data_load_by_reference_tester.yaml +9 -0
- pytest_scenario_files-0.12/tests/pytester_example_files/data_load_indirect_fixture_tester_1.yaml +7 -0
- pytest_scenario_files-0.12/tests/pytester_example_files/data_load_multi_files_tester_1.yaml +7 -0
- pytest_scenario_files-0.12/tests/pytester_example_files/data_load_multi_files_tester_2.json +10 -0
- pytest_scenario_files-0.12/tests/pytester_example_files/data_load_multi_files_tester_3.yaml +3 -0
- pytest_scenario_files-0.12/tests/pytester_example_files/data_load_multi_values_tester.yaml +7 -0
- pytest_scenario_files-0.12/tests/pytester_example_files/data_load_one_file_tester.json +6 -0
- pytest_scenario_files-0.12/tests/pytester_example_files/data_load_one_tester_1.json +6 -0
- pytest_scenario_files-0.12/tests/pytester_example_files/data_load_one_tester_1.yaml +7 -0
- pytest_scenario_files-0.12/tests/pytester_example_files/data_load_subdirs_tester_1.yaml +9 -0
- pytest_scenario_files-0.12/tests/pytester_example_files/data_load_subdirs_tester_2.json +12 -0
- pytest_scenario_files-0.12/tests/pytester_example_files/data_merge_multi_fixtures_tester_1.yaml +5 -0
- pytest_scenario_files-0.12/tests/pytester_example_files/data_merge_multi_fixtures_tester_2.json +8 -0
- pytest_scenario_files-0.12/tests/pytester_example_files/data_merge_multi_fixtures_tester_3.yaml +20 -0
- pytest_scenario_files-0.12/tests/pytester_example_files/data_test_case_not_dict_tester.yaml +1 -0
- pytest_scenario_files-0.12/tests/pytester_example_files/example_test_data_file_not_dict_tester.py +2 -0
- pytest_scenario_files-0.12/tests/pytester_example_files/example_test_data_file_wrong_ext_tester.py +2 -0
- pytest_scenario_files-0.12/tests/pytester_example_files/example_test_fixture_merge_conflict_tester.py +2 -0
- pytest_scenario_files-0.12/tests/pytester_example_files/example_test_fixture_mismatch_tester.py +2 -0
- pytest_scenario_files-0.12/tests/pytester_example_files/example_test_ignore_multi_files_tester.py +2 -0
- pytest_scenario_files-0.12/tests/pytester_example_files/example_test_load_by_reference_tester.py +2 -0
- pytest_scenario_files-0.12/tests/pytester_example_files/example_test_load_indirect_fixture_tester.py +12 -0
- pytest_scenario_files-0.12/tests/pytester_example_files/example_test_load_multi_files_tester.py +2 -0
- pytest_scenario_files-0.12/tests/pytester_example_files/example_test_load_multi_values_tester.py +2 -0
- pytest_scenario_files-0.12/tests/pytester_example_files/example_test_load_one_file_tester.py +4 -0
- pytest_scenario_files-0.12/tests/pytester_example_files/example_test_load_one_tester.py +4 -0
- pytest_scenario_files-0.12/tests/pytester_example_files/example_test_load_subdirs_tester.py +2 -0
- pytest_scenario_files-0.12/tests/pytester_example_files/example_test_merge_multi_fixtures_tester.py +2 -0
- pytest_scenario_files-0.12/tests/pytester_example_files/example_test_no_data_files_found_tester.py +2 -0
- pytest_scenario_files-0.12/tests/pytester_example_files/example_test_test_case_not_dict_tester.py +2 -0
- pytest_scenario_files-0.12/tests/success cases/test_ignore_multi_files.py +21 -0
- pytest_scenario_files-0.12/tests/success cases/test_load_by_reference.py +12 -0
- pytest_scenario_files-0.12/tests/success cases/test_load_file_extended_name.py +52 -0
- pytest_scenario_files-0.12/tests/success cases/test_load_indirect_fixture.py +15 -0
- pytest_scenario_files-0.12/tests/success cases/test_load_multi_files.py +20 -0
- pytest_scenario_files-0.12/tests/success cases/test_load_multi_values.py +17 -0
- pytest_scenario_files-0.12/tests/success cases/test_load_one_file.py +18 -0
- pytest_scenario_files-0.12/tests/success cases/test_load_separate_subdirs.py +23 -0
- pytest_scenario_files-0.12/tests/success cases/test_load_subdirs.py +16 -0
- pytest_scenario_files-0.12/tests/success cases/test_merge_multi_fixtures.py +20 -0
- pytest_scenario_files-0.12/tests/success cases/test_other_dir_load_by_reference.py +19 -0
- pytest_scenario_files-0.12/tests/test_plugin_load.py +12 -0
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
# Byte-compiled / optimized / DLL files
|
|
2
|
+
__pycache__/
|
|
3
|
+
*.py[cod]
|
|
4
|
+
*$py.class
|
|
5
|
+
|
|
6
|
+
# C extensions
|
|
7
|
+
*.so
|
|
8
|
+
|
|
9
|
+
# Distribution / packaging
|
|
10
|
+
.Python
|
|
11
|
+
env/
|
|
12
|
+
build/
|
|
13
|
+
develop-eggs/
|
|
14
|
+
dist/
|
|
15
|
+
downloads/
|
|
16
|
+
eggs/
|
|
17
|
+
.eggs/
|
|
18
|
+
lib/
|
|
19
|
+
lib64/
|
|
20
|
+
parts/
|
|
21
|
+
sdist/
|
|
22
|
+
var/
|
|
23
|
+
*.egg-info/
|
|
24
|
+
.installed.cfg
|
|
25
|
+
*.egg
|
|
26
|
+
|
|
27
|
+
# PyInstaller
|
|
28
|
+
# Usually these files are written by a python script from a template
|
|
29
|
+
# before PyInstaller builds the exe, so as to inject date/other infos into it.
|
|
30
|
+
*.manifest
|
|
31
|
+
*.spec
|
|
32
|
+
|
|
33
|
+
# Installer logs
|
|
34
|
+
pip-log.txt
|
|
35
|
+
pip-delete-this-directory.txt
|
|
36
|
+
|
|
37
|
+
# Unit test / coverage reports
|
|
38
|
+
htmlcov/
|
|
39
|
+
.tox/
|
|
40
|
+
.coverage
|
|
41
|
+
.coverage.*
|
|
42
|
+
.cache
|
|
43
|
+
nosetests.xml
|
|
44
|
+
coverage.xml
|
|
45
|
+
*,cover
|
|
46
|
+
.hypothesis/
|
|
47
|
+
.pytest_cache
|
|
48
|
+
|
|
49
|
+
# Translations
|
|
50
|
+
*.mo
|
|
51
|
+
*.pot
|
|
52
|
+
|
|
53
|
+
# Django stuff:
|
|
54
|
+
*.log
|
|
55
|
+
local_settings.py
|
|
56
|
+
|
|
57
|
+
# Flask instance folder
|
|
58
|
+
instance/
|
|
59
|
+
|
|
60
|
+
# Sphinx documentation
|
|
61
|
+
docs/_build/
|
|
62
|
+
|
|
63
|
+
# MkDocs documentation
|
|
64
|
+
/site/
|
|
65
|
+
|
|
66
|
+
# PyBuilder
|
|
67
|
+
target/
|
|
68
|
+
|
|
69
|
+
# IPython Notebook
|
|
70
|
+
.ipynb_checkpoints
|
|
71
|
+
|
|
72
|
+
# pyenv
|
|
73
|
+
.python-version
|
|
74
|
+
|
|
75
|
+
# hatch versioning
|
|
76
|
+
_version.py
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
# See https://pre-commit.com for more information
|
|
2
|
+
# See https://pre-commit.com/hooks.html for more hooks
|
|
3
|
+
repos:
|
|
4
|
+
- repo: https://github.com/astral-sh/ruff-pre-commit
|
|
5
|
+
# Ruff version.
|
|
6
|
+
rev: v0.1.14
|
|
7
|
+
hooks:
|
|
8
|
+
# Run the linter.
|
|
9
|
+
- id: ruff
|
|
10
|
+
args: [ --fix ]
|
|
11
|
+
# Run the formatter.
|
|
12
|
+
- id: ruff-format
|
|
13
|
+
- repo: https://github.com/pre-commit/pre-commit-hooks
|
|
14
|
+
rev: v4.5.0
|
|
15
|
+
hooks:
|
|
16
|
+
- id: trailing-whitespace
|
|
17
|
+
- id: end-of-file-fixer
|
|
18
|
+
- id: check-yaml
|
|
19
|
+
- id: check-added-large-files
|
|
20
|
+
- repo: https://github.com/executablebooks/mdformat
|
|
21
|
+
rev: 0.7.17 # Use the ref you want to point at
|
|
22
|
+
hooks:
|
|
23
|
+
- id: mdformat
|
|
24
|
+
args: [ --wrap, "72", --number ]
|
|
25
|
+
# Optionally add plugins
|
|
26
|
+
additional_dependencies:
|
|
27
|
+
- mdformat-gfm
|
|
28
|
+
- mdformat-black
|
|
29
|
+
- mdformat-config
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
# Change Log
|
|
2
|
+
|
|
3
|
+
#### 0.12 - 2024-05-12
|
|
4
|
+
|
|
5
|
+
Rename to pytest_scenario_files
|
|
6
|
+
|
|
7
|
+
#### 0.11 - 2024-02-14
|
|
8
|
+
|
|
9
|
+
Add ability to specify indirect parameterization
|
|
10
|
+
|
|
11
|
+
#### 0.10.2 - 2024-02-09
|
|
12
|
+
|
|
13
|
+
Add additional test case for finding files by reference
|
|
14
|
+
|
|
15
|
+
#### 0.10.1 - 2024-02-08
|
|
16
|
+
|
|
17
|
+
Search the entire space for files that load data by reference instead of
|
|
18
|
+
assuming that the file will be found in the same subdirectory
|
|
19
|
+
|
|
20
|
+
#### 0.10 - 2024-01-27
|
|
21
|
+
|
|
22
|
+
- Change to using internal merge for data files
|
|
23
|
+
- Remove undocumented feature that directories that start with "." would
|
|
24
|
+
not be searched for test data files
|
|
25
|
+
- Add expected fail test cases to 100% test coverage
|
|
26
|
+
- Remove dependency on `deepmerge`
|
|
27
|
+
|
|
28
|
+
#### 0.9.5 - 2024-01-25
|
|
29
|
+
|
|
30
|
+
Updates to documentation and formatting
|
|
31
|
+
|
|
32
|
+
#### 0.9.3 - 2024-01-24
|
|
33
|
+
|
|
34
|
+
Update how merging works
|
|
35
|
+
|
|
36
|
+
- Change merge strategy so that a conflict will raise an exception
|
|
37
|
+
- Correct the start directory for data file search
|
|
38
|
+
- Update documentation
|
|
39
|
+
|
|
40
|
+
#### 0.9 - 2024-01-22
|
|
41
|
+
|
|
42
|
+
Additional tests, examples, and documentation
|
|
43
|
+
|
|
44
|
+
#### 0.3 - 2024-01-20
|
|
45
|
+
|
|
46
|
+
Allow merging data for a test case from multiple files
|
|
47
|
+
|
|
48
|
+
- Introduce dependency on `deepmerge`
|
|
49
|
+
|
|
50
|
+
#### 0.2 - 2024-01-20
|
|
51
|
+
|
|
52
|
+
Change to multiple fixtures
|
|
53
|
+
|
|
54
|
+
- Load data into multiple fixtures rather than a single fixture
|
|
55
|
+
- Update tests and example code to work with the new paradigm
|
|
56
|
+
|
|
57
|
+
#### 0.1 - 2024-01-17
|
|
58
|
+
|
|
59
|
+
Initial checkpoint
|
|
60
|
+
|
|
61
|
+
- Load multiple test cases from one file
|
|
62
|
+
- Load multiple test cases from multiple files
|
|
63
|
+
- Unit tests via `pytester`
|
|
64
|
+
- Clean up unused files
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
|
|
2
|
+
The MIT License (MIT)
|
|
3
|
+
|
|
4
|
+
Copyright (c) 2024 Paul Suh
|
|
5
|
+
|
|
6
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
7
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
8
|
+
in the Software without restriction, including without limitation the rights
|
|
9
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
10
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
11
|
+
furnished to do so, subject to the following conditions:
|
|
12
|
+
|
|
13
|
+
The above copyright notice and this permission notice shall be included in
|
|
14
|
+
all copies or substantial portions of the Software.
|
|
15
|
+
|
|
16
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
17
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
18
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
19
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
20
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
21
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
|
22
|
+
THE SOFTWARE.
|
|
@@ -0,0 +1,373 @@
|
|
|
1
|
+
Metadata-Version: 2.3
|
|
2
|
+
Name: pytest-scenario-files
|
|
3
|
+
Version: 0.12
|
|
4
|
+
Summary: A pytest plugin that generates unit test scenarios from data files.
|
|
5
|
+
Project-URL: Repository, https://github.com/paulsuh/pytest-scenario-files
|
|
6
|
+
Project-URL: Issues, https://github.com/paulsuh/pytest-scenario-files/issues
|
|
7
|
+
Project-URL: Changelog, https://github.com/paulsuh/pytest-scenario-files/blob/main/CHANGES.md
|
|
8
|
+
Author-email: Paul Suh <paul@mspex.net>
|
|
9
|
+
License-File: LICENSE
|
|
10
|
+
Classifier: Development Status :: 4 - Beta
|
|
11
|
+
Classifier: Framework :: Pytest
|
|
12
|
+
Classifier: Intended Audience :: Developers
|
|
13
|
+
Classifier: License :: OSI Approved :: MIT License
|
|
14
|
+
Classifier: Operating System :: OS Independent
|
|
15
|
+
Classifier: Programming Language :: Python
|
|
16
|
+
Classifier: Programming Language :: Python :: 3
|
|
17
|
+
Classifier: Programming Language :: Python :: 3 :: Only
|
|
18
|
+
Classifier: Programming Language :: Python :: 3.9
|
|
19
|
+
Classifier: Programming Language :: Python :: 3.10
|
|
20
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
21
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
22
|
+
Classifier: Programming Language :: Python :: Implementation :: CPython
|
|
23
|
+
Classifier: Programming Language :: Python :: Implementation :: PyPy
|
|
24
|
+
Classifier: Topic :: Software Development :: Testing
|
|
25
|
+
Requires-Python: >=3.9
|
|
26
|
+
Requires-Dist: pytest>=7.2.0
|
|
27
|
+
Requires-Dist: pyyaml
|
|
28
|
+
Provides-Extra: precommit
|
|
29
|
+
Requires-Dist: ruff; extra == 'precommit'
|
|
30
|
+
Provides-Extra: unittest
|
|
31
|
+
Requires-Dist: pytest; extra == 'unittest'
|
|
32
|
+
Requires-Dist: pytest-cov; extra == 'unittest'
|
|
33
|
+
Description-Content-Type: text/markdown
|
|
34
|
+
|
|
35
|
+
# pytest-scenario-files
|
|
36
|
+
|
|
37
|
+
***Making Pytest Scenarios Easy and Scalable***
|
|
38
|
+
|
|
39
|
+
![PyPI pyversions][link01] ![Pytest][link02]
|
|
40
|
+
|
|
41
|
+
![Hatch project][link03] ![Ruff][link04] ![Pre-Commit][link05]
|
|
42
|
+
|
|
43
|
+
[`pytest-scenario-files`][link06] is a [`pytest`][link07] plugin that
|
|
44
|
+
runs unit test scenarios using data loaded from files.
|
|
45
|
+
|
|
46
|
+
______________________________________________________________________
|
|
47
|
+
|
|
48
|
+
## Introduction
|
|
49
|
+
|
|
50
|
+
`pytest` has a feature called parameterization that allows you to run
|
|
51
|
+
the same test function repeatedly using different inputs to test
|
|
52
|
+
multiple scenarios. However, managing the test data for parameterization
|
|
53
|
+
can be a problem. Sometimes the input data is very large or there are
|
|
54
|
+
many test cases, so that it is impractical to put all of the data into
|
|
55
|
+
the source code of the test.
|
|
56
|
+
|
|
57
|
+
This plug-in loads the data for the test scenarios from separate data
|
|
58
|
+
files that are automatically matched up against the test functions. Each
|
|
59
|
+
function can have one or more data files associated with it, and each
|
|
60
|
+
file can contain multiple scenarios. The data files can be in JSON or
|
|
61
|
+
YAML format.
|
|
62
|
+
|
|
63
|
+
An additional issue with the basic pytest parameterization API is how
|
|
64
|
+
the user must provide the parameters. First all of the test case fixture
|
|
65
|
+
names in a list, followed by a list of lists with the values the
|
|
66
|
+
fixtures will take on, and then an optional list of test case id's.
|
|
67
|
+
Since the labels, values, and test case id's are in separate lists it
|
|
68
|
+
can be difficult to keep track of which fixture corresponds to which
|
|
69
|
+
value if you have many of them, and also which group of values
|
|
70
|
+
corresponds to which test id. The file structure uses a dict to keep the
|
|
71
|
+
test case id's, fixture names, and data values together in a way that is
|
|
72
|
+
easier on the human brain.
|
|
73
|
+
|
|
74
|
+
### Features
|
|
75
|
+
|
|
76
|
+
- Loads data for scenarios from files into fixtures
|
|
77
|
+
- Multiple scenario data sets may be in one file
|
|
78
|
+
- There may be multiple data files for each test
|
|
79
|
+
- Fixtures may refer to fixtures in other files
|
|
80
|
+
- Can specify indirect parameterization
|
|
81
|
+
- Intuitive and sane data file structure
|
|
82
|
+
|
|
83
|
+
### Compatibility
|
|
84
|
+
|
|
85
|
+
This package is a plug-in for `pytest` and works with Python 3.9 and up.
|
|
86
|
+
|
|
87
|
+
- Tested with `pytest` version 7.4.x, should work with any version 6.2.5
|
|
88
|
+
or higher
|
|
89
|
+
- Tested with CPython 3.9-3.12 and PyPy 3.9-3.10
|
|
90
|
+
|
|
91
|
+
While this code currently has a classifier of "Development Status :: 4 -
|
|
92
|
+
Beta", it is solid and well-tested. I will likely promote it to
|
|
93
|
+
"Development Status :: 5 - Production/Stable" after a little more
|
|
94
|
+
real-world usage.
|
|
95
|
+
|
|
96
|
+
______________________________________________________________________
|
|
97
|
+
|
|
98
|
+
## Installation
|
|
99
|
+
|
|
100
|
+
You can install `pytest-scenario-files` from PyPI by using `pip` :
|
|
101
|
+
|
|
102
|
+
```
|
|
103
|
+
$ pip install pytest-scenario-files
|
|
104
|
+
```
|
|
105
|
+
|
|
106
|
+
## Usage
|
|
107
|
+
|
|
108
|
+
To use this plugin you need to make only two changes to your project:
|
|
109
|
+
|
|
110
|
+
1. Install the plug-in
|
|
111
|
+
2. Create the data file(s) with the proper names and formats
|
|
112
|
+
|
|
113
|
+
You can then access the data from the files via the fixtures defined in
|
|
114
|
+
those files. The most common usage is to manage test case inputs and
|
|
115
|
+
expected results. This allows the developer to change and add test cases
|
|
116
|
+
without making changes to the test code.
|
|
117
|
+
|
|
118
|
+
Just as with pytest's basic parameterization, the test function must
|
|
119
|
+
have all of the fixtures in its parameter list. Otherwise, an exception
|
|
120
|
+
will be raised.
|
|
121
|
+
|
|
122
|
+
The unit tests for this package are good examples of possible ways to
|
|
123
|
+
use this package. Look in the files in the `tests/` directory and the
|
|
124
|
+
corresponding files in the `tests/pytester_example_files` directory.
|
|
125
|
+
|
|
126
|
+
This package is also designed to be transparent for non-parametric
|
|
127
|
+
usage. If there are no data files associated with a particular test the
|
|
128
|
+
fixtures will not be parameterized and everything will work as though
|
|
129
|
+
the plug-in was not present.
|
|
130
|
+
|
|
131
|
+
### Data File Matching
|
|
132
|
+
|
|
133
|
+
A data file will be loaded if it matches all of the following criteria:
|
|
134
|
+
|
|
135
|
+
1. The filename starts with `data_`, followed by the name of the test
|
|
136
|
+
function with the prefix `test_` removed. The remainder of the
|
|
137
|
+
filename may be any value, and is usually used to identify the tests
|
|
138
|
+
contained in the file.
|
|
139
|
+
2. The filename must end in `.json`, `.yaml`, or `.yml`.
|
|
140
|
+
3. The file is contained in a folder at or below the file that contains
|
|
141
|
+
the test.
|
|
142
|
+
|
|
143
|
+
For example, for a test function
|
|
144
|
+
|
|
145
|
+
```
|
|
146
|
+
test_foo(...)
|
|
147
|
+
```
|
|
148
|
+
|
|
149
|
+
the files
|
|
150
|
+
|
|
151
|
+
```
|
|
152
|
+
data_foo_part_1.json
|
|
153
|
+
data_foo_part_2.yaml
|
|
154
|
+
subfolder/data_foo.yaml
|
|
155
|
+
```
|
|
156
|
+
|
|
157
|
+
would all be loaded.
|
|
158
|
+
|
|
159
|
+
*Caution*: Beware of situations where the name of one test is an
|
|
160
|
+
extended version of another. E.g., if you have two tests named
|
|
161
|
+
`test_foo()` and `test_foo_bar()`, a data file with the name
|
|
162
|
+
`data_foo_bar.yaml` will be loaded for *both* tests. To prevent this,
|
|
163
|
+
split the two test functions into two separate files in two different
|
|
164
|
+
directories or change the name of one of the test functions. See
|
|
165
|
+
`test_load_file_extended_name.py` and `test_load_separate_subdirs.py` in
|
|
166
|
+
the unit test files for this package for concrete examples of what might
|
|
167
|
+
happen and how to avoid it.
|
|
168
|
+
|
|
169
|
+
### Data File Structure
|
|
170
|
+
|
|
171
|
+
Each data file may contain one or more sets of test data, in either yaml
|
|
172
|
+
or json format. The top level is a dict whose keys are the test id's.
|
|
173
|
+
Each test id is a dict whose keys are fixture names and whose values are
|
|
174
|
+
the test data. The test data may be anything, including container types
|
|
175
|
+
such as lists or dicts. An example input file `data_foo_bar.yaml` might
|
|
176
|
+
contain:
|
|
177
|
+
|
|
178
|
+
```yaml
|
|
179
|
+
test1:
|
|
180
|
+
input_data_1: 17
|
|
181
|
+
input_data_2: 3
|
|
182
|
+
expected_result: 51
|
|
183
|
+
|
|
184
|
+
test2:
|
|
185
|
+
input_data_1:
|
|
186
|
+
- abc
|
|
187
|
+
input_data_2: 3
|
|
188
|
+
expected_result:
|
|
189
|
+
- abc
|
|
190
|
+
- abc
|
|
191
|
+
- abc
|
|
192
|
+
```
|
|
193
|
+
|
|
194
|
+
This would parameterize into two test cases labeled `test1` and `test2`,
|
|
195
|
+
each with three fixtures, `input_data_1`, `input_data_2`, and
|
|
196
|
+
`expected_result`.
|
|
197
|
+
|
|
198
|
+
### Test Case Merging and Conflicts
|
|
199
|
+
|
|
200
|
+
If the same test case id is present in two different files, the fixtures
|
|
201
|
+
from the two files will be merged as long as a fixture with the same
|
|
202
|
+
name is not defined more than once for any particular test case id. For
|
|
203
|
+
example, for a test function named `test_foo()` with two data files:
|
|
204
|
+
|
|
205
|
+
File `data_foo_1.yaml`;
|
|
206
|
+
|
|
207
|
+
```yaml
|
|
208
|
+
test_case_one:
|
|
209
|
+
fixture_one: 17
|
|
210
|
+
```
|
|
211
|
+
|
|
212
|
+
File `data_foo_2.yaml`;
|
|
213
|
+
|
|
214
|
+
```yaml
|
|
215
|
+
test_case_one:
|
|
216
|
+
fixture_two: 170
|
|
217
|
+
```
|
|
218
|
+
|
|
219
|
+
The function will be passed two fixtures `fixture_one=17` and
|
|
220
|
+
`fixture_two=170` for a test case with `id=test_case_one`.
|
|
221
|
+
|
|
222
|
+
*However*, if the fixture names are the same there will be a conflict
|
|
223
|
+
and the code that merges the test cases will raise an exception.
|
|
224
|
+
|
|
225
|
+
### Loading Fixture Values by Reference
|
|
226
|
+
|
|
227
|
+
An additional powerful feature is the ability to load the value for a
|
|
228
|
+
fixture from another data file. You can have fixture data loaded from
|
|
229
|
+
another file by setting the fixture value to a specially formatted
|
|
230
|
+
string. It must be prefixed with two underscores and be of the format:
|
|
231
|
+
|
|
232
|
+
```
|
|
233
|
+
__<Filename>:<test case id>:<fixture name>
|
|
234
|
+
```
|
|
235
|
+
|
|
236
|
+
For instance, a data file `data_other_check_3.yaml` might reference the
|
|
237
|
+
data file `data_foo_2.yaml` from the previous section:
|
|
238
|
+
|
|
239
|
+
```yaml
|
|
240
|
+
check_functionality:
|
|
241
|
+
input_data_1: 42
|
|
242
|
+
other_data: __data_foo_2.yaml:test_case_one:fixture_two
|
|
243
|
+
```
|
|
244
|
+
|
|
245
|
+
This would result in two fixture values being sent into the test
|
|
246
|
+
function, `input_data_1 = 42` and `other_data = 170`, for a test case
|
|
247
|
+
with `id = check_functionality`. (Note that there is nothing preventing
|
|
248
|
+
an infinite self-referential loop although that is something that should
|
|
249
|
+
be avoided).
|
|
250
|
+
|
|
251
|
+
### Indirect Parameterization
|
|
252
|
+
|
|
253
|
+
Pytest has a feature called [indirect parameterization][link08], where
|
|
254
|
+
the parameter value is passed to a fixture function, and the return
|
|
255
|
+
value of the fixture function is then passed downstream. You can specify
|
|
256
|
+
that a fixture should be marked for indirect parameterization by
|
|
257
|
+
appending the suffix `_indirect` to the fixture name in the data file.
|
|
258
|
+
If the data file contains:
|
|
259
|
+
|
|
260
|
+
```yaml
|
|
261
|
+
test_case_1:
|
|
262
|
+
variable_A: 51
|
|
263
|
+
variable_B_indirect: 3
|
|
264
|
+
|
|
265
|
+
test_case_2:
|
|
266
|
+
variable_A: 85
|
|
267
|
+
variable_B_indirect: 5
|
|
268
|
+
```
|
|
269
|
+
|
|
270
|
+
the corresponding test code would be:
|
|
271
|
+
|
|
272
|
+
```python
|
|
273
|
+
@pytest.fixture
|
|
274
|
+
def variable_B(request):
|
|
275
|
+
return request.param * 17
|
|
276
|
+
|
|
277
|
+
|
|
278
|
+
def test_func(variable_A, variable_B):
|
|
279
|
+
assert variable_A == variable_B
|
|
280
|
+
```
|
|
281
|
+
|
|
282
|
+
The values for fixture `variable_A` would be passed directly to
|
|
283
|
+
`test_func()`, but the values for `variable_B_indirect` would be passed
|
|
284
|
+
to the `variable_B()` function and the return value would be passed in
|
|
285
|
+
as the `variable_B` parameter to `test_func()`.
|
|
286
|
+
|
|
287
|
+
______________________________________________________________________
|
|
288
|
+
|
|
289
|
+
## Reporting Issues
|
|
290
|
+
|
|
291
|
+
If you encounter any problems, please [file an issue][link09] including
|
|
292
|
+
a detailed description and (if possible) an example of the problem.
|
|
293
|
+
|
|
294
|
+
## Contributing
|
|
295
|
+
|
|
296
|
+
Since this project is a pytest plug-in, it really does require
|
|
297
|
+
test-driven development. If you want to contribute a bug fix or new
|
|
298
|
+
feature, please first create a test case that demonstrates what your new
|
|
299
|
+
code is supposed to do. Note that you need to set things up using the
|
|
300
|
+
`pytester` fixture, rather than testing directly.
|
|
301
|
+
|
|
302
|
+
This project uses [hatch][link10] for its environments and build system,
|
|
303
|
+
as well as [pre-commit][link11], [ruff][link12], and [mdformat][link13]
|
|
304
|
+
for formatting and linting. Before you send in a pull request, please:
|
|
305
|
+
|
|
306
|
+
- Set up `pre-commit` and use it to run `ruff` and `mdformat` with the
|
|
307
|
+
settings included in the `pyproject.toml` and
|
|
308
|
+
`.pre-commit-config.yaml` files
|
|
309
|
+
- Run tests using the command `hatch run test:test`, which will run all
|
|
310
|
+
of the tests against CPython 3.9-3.12 and PyPy 3.9-3.10
|
|
311
|
+
- Check test coverage with `hatch run cov`
|
|
312
|
+
|
|
313
|
+
## License
|
|
314
|
+
|
|
315
|
+
Distributed under the terms of the `MIT` license,
|
|
316
|
+
`pytest-scenario-files` is free and open source software.
|
|
317
|
+
|
|
318
|
+
## Colophon
|
|
319
|
+
|
|
320
|
+
This plug-in was originally named `pytest-parameterize-from-files`. It
|
|
321
|
+
was inspired by the pytest plug-ins `pytest-datadir`,
|
|
322
|
+
`pytest-datafixtures`, and `pytest-xpara`. I also later found the
|
|
323
|
+
non-plug-in package `parameterize-from-file`. To avoid confusion and
|
|
324
|
+
provide a more descriptive title, I renamed this project to
|
|
325
|
+
`pytest-scenario-files`.
|
|
326
|
+
|
|
327
|
+
- I wanted to load data from files without having to write any
|
|
328
|
+
additional code. However, `pytest-datadir` and `pytest-datafixtures`
|
|
329
|
+
required code in the test or fixtures specifically to read in the
|
|
330
|
+
file.
|
|
331
|
+
- I liked the way that `pytest-xpara` loaded data into a fixture, but
|
|
332
|
+
didn't like that it would only work with one file and that I had to
|
|
333
|
+
specify the file on the command line.
|
|
334
|
+
- After I wrote much of this project I found the package
|
|
335
|
+
`parameterize-from-files` which has a similar name. It's a powerful
|
|
336
|
+
and capable tool, but it's not to my taste as I think it's trying too
|
|
337
|
+
hard.
|
|
338
|
+
- It requires a decorator per test function, with potentially complex
|
|
339
|
+
syntax inside the decorator's arguments.
|
|
340
|
+
- It lets the user place code snippets into the data files which will
|
|
341
|
+
be a maintenance problem down the road. It's cleaner to take
|
|
342
|
+
advantage of Pytest's indirect parameterization feature instead.
|
|
343
|
+
- Having to import the package in every test file and decorate each
|
|
344
|
+
function increases the complexity of the test code.
|
|
345
|
+
|
|
346
|
+
In general I wanted the data file handling to be scalable:
|
|
347
|
+
|
|
348
|
+
- If you have 50 unit tests you don't have to specify all 50 files to
|
|
349
|
+
load in code or on the command line.
|
|
350
|
+
- You can reference data from other files to keep duplication low.
|
|
351
|
+
|
|
352
|
+
This `pytest` plugin was developed using a skeleton generated by
|
|
353
|
+
[`cookiecutter`][link14] along with the
|
|
354
|
+
[`cookiecutter-pytest-plugin`][link15] template, then extensively
|
|
355
|
+
modified to bring it up to modern standards.
|
|
356
|
+
|
|
357
|
+
______________________________________________________________________
|
|
358
|
+
|
|
359
|
+
[link01]: https://img.shields.io/pypi/pyversions/pytest-parameterize-from-files.svg
|
|
360
|
+
[link02]: https://img.shields.io/badge/Pytest-Plug--in-orange?logo=pytest
|
|
361
|
+
[link03]: https://img.shields.io/badge/%F0%9F%A5%9A-Hatch-4051b5.svg
|
|
362
|
+
[link04]: https://img.shields.io/endpoint?url=https://raw.githubusercontent.com/astral-sh/ruff/main/assets/badge/v2.json
|
|
363
|
+
[link05]: https://img.shields.io/badge/pre--commit-enabled-brightgreen?logo=pre-commit
|
|
364
|
+
[link06]: https://github.com/paulsuh/pytest-scenario-files
|
|
365
|
+
[link07]: https://docs.pytest.org/en/stable/index.html
|
|
366
|
+
[link08]: https://docs.pytest.org/en/stable/example/parametrize.html#indirect-parametrization
|
|
367
|
+
[link09]: https://github.com/paulsuh/pytest-scenario-files/issues
|
|
368
|
+
[link10]: https://github.com/pypa/hatch
|
|
369
|
+
[link11]: https://pre-commit.com
|
|
370
|
+
[link12]: https://github.com/astral-sh/ruff
|
|
371
|
+
[link13]: https://github.com/executablebooks/mdformat
|
|
372
|
+
[link14]: https://pypi.org/project/cookiecutter/
|
|
373
|
+
[link15]: https://github.com/pytest-dev/cookiecutter-pytest-plugin
|