Compare commits
5 Commits
Author | SHA1 | Date | |
---|---|---|---|
4eeeef5353 | |||
87ca957c68 | |||
f370ac7ce2 | |||
45abe33d7f | |||
e0733a2ce6 |
11
CONTRIBUTING.md
Normal file
11
CONTRIBUTING.md
Normal file
@ -0,0 +1,11 @@
|
||||
Contributions are always welcome. There are different ways may help:
|
||||
|
||||
- Reporting bugs and errors
|
||||
- Suggesting new features
|
||||
- Improving code
|
||||
- Improving documentation
|
||||
- Implementing new features
|
||||
|
||||
The instance [git.codebau.dev](https://git.codebau.dev/) does not accept new registrations currently. Please contact me
|
||||
under `csv_params`(at)`jued.de` if you have any contribution. We'll send you an invitation for the source code server or
|
||||
find another way.
|
116
README.md
116
README.md
@ -1,3 +1,5 @@
|
||||
![pytest-csv-params](docs/icon/pytest-csv-params.png)
|
||||
|
||||
# pytest-csv-params
|
||||
|
||||
A pytest plugin to parametrize data-driven tests by CSV files.
|
||||
@ -28,6 +30,11 @@ pip install pytest-csv-params
|
||||
poetry add --dev pytest-csv-params
|
||||
```
|
||||
|
||||
## Documentation / User Guide
|
||||
|
||||
**Detailed documentation can be found under
|
||||
[docs.codebau.dev/pytest-plugins/pytest-csv-params/](https://docs.codebau.dev/pytest-plugins/pytest-csv-params/)**
|
||||
|
||||
## Usage: Command Line Argument
|
||||
|
||||
| Argument | Required | Description | Example |
|
||||
@ -36,7 +43,7 @@ poetry add --dev pytest-csv-params
|
||||
|
||||
## Usage: Decorator
|
||||
|
||||
Simply decorate your test method with `@csv_params` and the following parameters:
|
||||
Simply decorate your test method with `@csv_params` (`pytest_csv_params.decorator.csv_params`) and the following parameters:
|
||||
|
||||
| Parameter | Type | Description | Example |
|
||||
|------------------|--------------------------|----------------------------------------------------------------------------------------------------------------------------------------|------------------------------------------------------------------------------------------------|
|
||||
@ -47,43 +54,6 @@ Simply decorate your test method with `@csv_params` and the following parameters
|
||||
| `data_casts` | `dict` (optional) | Cast Methods for the CSV Data (see "Data Casting" below) | `{ "a": int, "b": float }` |
|
||||
| `header_renames` | `dict` (optional) | Replace headers from the CSV file, so that they can be used as parameters for the test function (since 0.3.0) | `{ "Annual Amount of Bananas": "banana_count", "Cherry export price": "cherry_export_price" }` |
|
||||
|
||||
## CSV File Lookup Order
|
||||
|
||||
CSV files are looked up following this rules:
|
||||
|
||||
- If the `data_file` parameter is an absolute path, this is used, regardless of the `base_dir` parameter or command line argument.
|
||||
- If the `data_file` parameter is relative:
|
||||
- If the `base_dir` parameter is set, the file is looked up there, regardless of the command line argument
|
||||
- If the `base_dir` parameter is not set (or `None`):
|
||||
- If the command line argument is set, the file is looked up there
|
||||
- If the command line argument is not set, the file is looked up in the current working directory
|
||||
|
||||
## Data Casting
|
||||
|
||||
When data is read from CSV, they are always parsed as `str`. If you need them in other formats, you can set a method that should be called with the value.
|
||||
|
||||
These methods can also be lambdas, and are also good for further transformations.
|
||||
|
||||
### Data Casting Example
|
||||
|
||||
```python
|
||||
from pytest_csv_params.decorator import csv_params
|
||||
|
||||
def normalize(x: str) -> str:
|
||||
return x.strip().upper()
|
||||
|
||||
@csv_params(
|
||||
data_file="/test/data.csv",
|
||||
data_casts={
|
||||
"col_x": normalize,
|
||||
"col_y": float,
|
||||
},
|
||||
)
|
||||
def test_something(col_x, col_y):
|
||||
# Test something...
|
||||
...
|
||||
```
|
||||
|
||||
## CSV Format
|
||||
|
||||
The default CSV format is:
|
||||
@ -93,31 +63,6 @@ The default CSV format is:
|
||||
- If you need a `"` in the value, use `""` (double quote)
|
||||
- Fields are separated by comma (`,`)
|
||||
|
||||
**The first line must contain the row names. Row names must match the parameters of the test method (except for an ID column that is configured as such -- see `id_col` decorator parameter).**
|
||||
|
||||
### Example CSV
|
||||
|
||||
```text
|
||||
"ID#", "part_a", "part_b", "expected_result"
|
||||
"first", 1, 2, 3
|
||||
"second", 3, 4, 7
|
||||
"third", 10, 11, 21
|
||||
```
|
||||
|
||||
### Headers
|
||||
|
||||
The header line is very important, as it maps the values to parameters of the test function. The plugin supports you with that. The following rules apply:
|
||||
|
||||
- Every character that is not valid in a variable name is replaced by an underscore (`_`)
|
||||
- If the character at the start is not a letter or an underscore, it is replaced by an underscore(`_`)
|
||||
- If the name is still invalid then, because it's a keyword or a builtin name, an exception is raised (`CsvHeaderNameInvalid`)
|
||||
|
||||
If you don't want to change your CSV file, you can use the `header_renames` parameter to the decorator to rename headers as needed.
|
||||
|
||||
Headers must be unique, and an Exception is raised if not (`CsvHeaderNameInvalid`).
|
||||
|
||||
The header handling was heavily improved in Version 0.3.0.
|
||||
|
||||
## Usage Example
|
||||
|
||||
This example uses the CSV example from above.
|
||||
@ -220,29 +165,12 @@ def test_container_size_is_big_enough(
|
||||
...
|
||||
```
|
||||
|
||||
## Breaking Changes
|
||||
## Changelog
|
||||
|
||||
### Version 0.3.0
|
||||
- A detailed changelog is here:
|
||||
[docs.codebau.dev/pytest-plugins/pytest-csv-params/pages/changelog.html](https://docs.codebau.dev/pytest-plugins/pytest-csv-params/pages/changelog.html)
|
||||
|
||||
- Column header names that are reserved keywords or builtin names are no longer accepted. You should have been in trouble already if you used them, so nothing should go wrong with this change and existing tests.
|
||||
|
||||
### Version 0.2.0
|
||||
|
||||
- The parameter order for `pytest_csv_params.decorator.csv_params` changed to allow the shorthand usage with only a `data_file` as positional argument. If you used keyword arguments only (like the docs recommend), you will not run into trouble.
|
||||
|
||||
## Contributing
|
||||
|
||||
### Build and test
|
||||
|
||||
You need [Poetry](https://python-poetry.org/) in order to build this project.
|
||||
|
||||
Tests are implemented with `pytest`, and `tox` is used to orchestrate them for the supported python versions.
|
||||
|
||||
- Checkout this repo
|
||||
- Run `poetry install`
|
||||
- Run `poetry run tox` (for all supported python versions) or `poetry run pytest` (for your current version)
|
||||
|
||||
### Bugs etc.
|
||||
## Bugs etc.
|
||||
|
||||
Please send your issues to `csv-params_issues` (at) `jued.de`. Please include the following:
|
||||
|
||||
@ -252,24 +180,16 @@ Please send your issues to `csv-params_issues` (at) `jued.de`. Please include th
|
||||
|
||||
It would be great if you could include example code that clarifies your issue.
|
||||
|
||||
### Pull Requests
|
||||
See [CONTRIBUTING.md](CONTRIBUTING.md) for details.
|
||||
|
||||
## Pull Requests
|
||||
|
||||
Pull requests are always welcome. Since this Gitea instance is not open to public, just send an e-mail to discuss options.
|
||||
|
||||
Any changes that are made are to be backed by tests. Please give me a sign if you're going to break the existing API and let us discuss ways to handle that.
|
||||
|
||||
### Quality Measures
|
||||
See [CONTRIBUTING.md](CONTRIBUTING.md) for details.
|
||||
|
||||
Backed with pytest plugins:
|
||||
## Where are the sources?
|
||||
|
||||
- Pylint _(static code analysis and best practises)_
|
||||
- black _(code formatting standards)_
|
||||
- isort _(keep imports sorted)_
|
||||
- Bandit _(basic static security tests)_
|
||||
- mypy _(typechecking)_
|
||||
|
||||
Please to a complete pytest run (`poetry run pytest`), and consider running it on all supported platforms with (`poetry run tox`).
|
||||
|
||||
## License
|
||||
|
||||
Code is under MIT license. See `LICENSE.txt` for details.
|
||||
The source code is available under [git.codebau.dev/pytest-plugins/pytest-csv-params](https://git.codebau.dev/pytest-plugins/pytest-csv-params).
|
||||
|
@ -1,14 +1,22 @@
|
||||
"""
|
||||
Command Line Options
|
||||
This pytest plugin requires command line arguments that are parsed from the pytest framework. This module contains code
|
||||
to instruct pytest to deliver the required values.
|
||||
"""
|
||||
|
||||
from _pytest.config.argparsing import Parser
|
||||
|
||||
HELP_TEXT = "set base dir for getting CSV data files from"
|
||||
"""
|
||||
This is the help text for the command line arguments that is added by :meth:`pytest_addoption`.
|
||||
"""
|
||||
|
||||
|
||||
def pytest_addoption(parser: Parser, plugin_name: str = "csv-params") -> None:
|
||||
"""
|
||||
Add Command Line Arguments for pytest execution
|
||||
Entrypoint for pytest to extend the own :class:`Parser` with the things we need extra.
|
||||
|
||||
:param parser: The pytest command line argument parser
|
||||
:param plugin_name: The name of our plugin, with default value
|
||||
"""
|
||||
|
||||
group = parser.getgroup(plugin_name)
|
||||
|
@ -1,6 +1,8 @@
|
||||
"""
|
||||
Pytest Plugin Configuration
|
||||
The pytest plugin needs a setup (:meth:`pytest_configure`) and a teardown (:meth:`pytest_unconfigure`) method
|
||||
registered. This module contains the required methods for that.
|
||||
"""
|
||||
|
||||
from _pytest.config import Config
|
||||
|
||||
from _ptcsvp.plugin import Plugin
|
||||
@ -9,6 +11,9 @@ from _ptcsvp.plugin import Plugin
|
||||
def pytest_configure(config: Config, plugin_name: str = "csv_params") -> None:
|
||||
"""
|
||||
Register our Plugin
|
||||
|
||||
:param config: Pytets configuration class
|
||||
:param plugin_name: The name of the pytest plugin, with default value
|
||||
"""
|
||||
config.pluginmanager.register(Plugin(config), f"{plugin_name}_plugin")
|
||||
|
||||
@ -16,5 +21,8 @@ def pytest_configure(config: Config, plugin_name: str = "csv_params") -> None:
|
||||
def pytest_unconfigure(config: Config, plugin_name: str = "csv_params") -> None:
|
||||
"""
|
||||
Remove our Plugin
|
||||
|
||||
:param config: Pytest configuration class
|
||||
:param plugin_name: The name of the pytest plgin, with default value
|
||||
"""
|
||||
config.pluginmanager.unregister(f"{plugin_name}_plugin")
|
||||
|
@ -22,7 +22,7 @@ from pytest_csv_params.types import BaseDir, CsvDialect, DataCasts, DataFile, He
|
||||
|
||||
class TestCaseParameters(TypedDict):
|
||||
"""
|
||||
Type for Test Case
|
||||
Type for a single test case. Contains the optional :attr:`test_id` and the test :attr:`data`.
|
||||
"""
|
||||
|
||||
test_id: Optional[str]
|
||||
@ -32,6 +32,13 @@ class TestCaseParameters(TypedDict):
|
||||
def read_csv(base_dir: BaseDir, data_file: DataFile, dialect: CsvDialect) -> List[List[str]]:
|
||||
"""
|
||||
Get Data from CSV
|
||||
|
||||
:param base_dir: Optional directory to look up non-absolute CSV files (given as :attr:`data_file`)
|
||||
:param data_file: The CSV file to read. If this is an absolute path, :attr:`base_dir` will be ignored; if not, the
|
||||
:attr:`base_dir` is prepended.
|
||||
:param dialect: The CSV file dialect (definition of the format of a CSV file).
|
||||
|
||||
:returns: A list of rows, each row contains a list of columns; all type `str`
|
||||
"""
|
||||
|
||||
if data_file is None:
|
||||
@ -58,6 +65,14 @@ def read_csv(base_dir: BaseDir, data_file: DataFile, dialect: CsvDialect) -> Lis
|
||||
def clean_headers(current_headers: List[str], replacing: HeaderRenames) -> List[str]:
|
||||
"""
|
||||
Clean the CSV file headers
|
||||
|
||||
:param current_headers: List of the current headers, as read from the CSV file (without the ID column, as far as it
|
||||
exists)
|
||||
:param replacing: Dictionary of replacements for headers
|
||||
|
||||
:returns: List of cleaned header names
|
||||
|
||||
:raises CsvHeaderNameInvalid: When non-unique names appear
|
||||
"""
|
||||
if replacing is not None:
|
||||
for index, header in enumerate(current_headers):
|
||||
@ -79,7 +94,18 @@ def add_parametrization( # pylint: disable=too-many-arguments
|
||||
header_renames: HeaderRenames = None,
|
||||
) -> MarkDecorator:
|
||||
"""
|
||||
Get data from the files and add things to the tests
|
||||
Parametrize a test function with data from a CSV file.
|
||||
|
||||
For the public decorator, see :meth:`pytest_csv_params.decorator.csv_params`.
|
||||
|
||||
:param data_file: The CSV file to read the data from
|
||||
:param base_dir: Optional base directory to look for non-absolute :attr:`data_file` in
|
||||
:param id_col: Optional name of a column that shall be used to make the test IDs from
|
||||
:param data_casts: Methods to cast a column's data into a format that is required for the test
|
||||
:param dialect: The CSV file dialect (CSV file format) to use
|
||||
:param header_renames: A dictonary mapping the header names from the CSV file to usable names for the tests
|
||||
|
||||
:returns: :meth:`pytest.mark.parametrize` mark decorator, filled with all the data from the CSV.
|
||||
"""
|
||||
if base_dir is None:
|
||||
base_dir = getattr(Plugin, BASE_DIR_KEY, None)
|
||||
|
@ -1,19 +1,27 @@
|
||||
"""
|
||||
The main Plugin implementation
|
||||
This module contains the main plugin class. By the time of writing, it is quite unspectacular.
|
||||
"""
|
||||
|
||||
from _pytest.config import Config
|
||||
|
||||
BASE_DIR_KEY = "__pytest_csv_plugins__config__base_dir"
|
||||
BASE_DIR_KEY = "__pytest_csv_params__config__base_dir"
|
||||
"""
|
||||
The class attribute key for :class:`Plugin` to store the base dir command line argument value.
|
||||
"""
|
||||
|
||||
|
||||
class Plugin: # pylint: disable=too-few-public-methods
|
||||
"""
|
||||
Plugin Class
|
||||
The main plugin class
|
||||
|
||||
Currently, this class is nothing more than the keeper of the value of the command line argument (as defined by
|
||||
:meth:`_ptcsvp.cmdline.pytest_addoption`.
|
||||
"""
|
||||
|
||||
def __init__(self, config: Config) -> None:
|
||||
"""
|
||||
Hold the pytest config
|
||||
Initialize the class, and simply store the value of the command line argument, as class attribute.
|
||||
|
||||
:param config: Pytest configuration
|
||||
"""
|
||||
setattr(Plugin, BASE_DIR_KEY, config.option.csv_params_base_dir)
|
||||
|
@ -1,5 +1,5 @@
|
||||
"""
|
||||
Test if a variable name is valid
|
||||
This module contains code to validate variable/argument/parameter names or to make them valid ones.
|
||||
"""
|
||||
|
||||
import builtins
|
||||
@ -10,12 +10,22 @@ from string import ascii_letters, digits
|
||||
from pytest_csv_params.exception import CsvHeaderNameInvalid
|
||||
|
||||
VALID_CHARS = ascii_letters + digits
|
||||
"""
|
||||
Valid characters a variable/parameter/argument name can consist of
|
||||
"""
|
||||
|
||||
VARIABLE_NAME = re.compile(r"^[a-zA-Z_][A-Za-z0-9_]{0,1023}$")
|
||||
"""
|
||||
Regular expression that defines a valid variable/parameter/argument name
|
||||
"""
|
||||
|
||||
|
||||
def is_valid_name(name: str) -> bool:
|
||||
"""
|
||||
Checks if the variable name is valid
|
||||
|
||||
:param name: The name to be checked
|
||||
:returns: `True`, when the name is valid
|
||||
"""
|
||||
if (
|
||||
keyword.iskeyword(name)
|
||||
@ -28,7 +38,12 @@ def is_valid_name(name: str) -> bool:
|
||||
|
||||
def make_name_valid(name: str, replacement_char: str = "_") -> str:
|
||||
"""
|
||||
Make a name valid
|
||||
Make a name a valid name by replacing invalid chars with the as :attr:`replacement_char` given char
|
||||
|
||||
:param name: The name to make a valid one
|
||||
:param replacement_char: The char to replace invalid chars with, default is an underscore `_`
|
||||
:returns: A valid name
|
||||
:raises CsvHeaderNameInvalid: If the fixed name is still an invalid name
|
||||
"""
|
||||
|
||||
fixed_name = name
|
||||
|
@ -1,5 +1,8 @@
|
||||
"""
|
||||
Check Version Information
|
||||
This module contains two methods to check if the python version is recent enough (:meth:`check_python_version`) and if
|
||||
the pytest version is recent enough (:meth:`check_pytest_version`).
|
||||
|
||||
During the setup phase of the plugin (see :mod:`pytest_csv_params.plugin`) these methods are called.
|
||||
"""
|
||||
import sys
|
||||
from typing import Tuple
|
||||
@ -11,6 +14,9 @@ from packaging.version import parse
|
||||
def check_python_version(min_version: Tuple[int, int] = (3, 8)) -> None:
|
||||
"""
|
||||
Check if the current version is at least 3.8
|
||||
|
||||
:param min_version: The minimum version required, as tuple, default is 3.8
|
||||
:raises PythonTooOldError: When the python version is too old/unsupported
|
||||
"""
|
||||
|
||||
if sys.version_info < min_version:
|
||||
@ -20,6 +26,9 @@ def check_python_version(min_version: Tuple[int, int] = (3, 8)) -> None:
|
||||
def check_pytest_version(min_version: Tuple[int, int] = (7, 1)) -> None:
|
||||
"""
|
||||
Check if the current version is at least 7.1
|
||||
|
||||
:param min_version: The minimum version required, as tuple, default is 7.1
|
||||
:raises RuntimeError: When the pytest version is too old/unsupported
|
||||
"""
|
||||
|
||||
from pytest import __version__ as pytest_version # pylint: disable=import-outside-toplevel
|
||||
|
20
docs/Makefile
Normal file
20
docs/Makefile
Normal file
@ -0,0 +1,20 @@
|
||||
# Minimal makefile for Sphinx documentation
|
||||
#
|
||||
|
||||
# You can set these variables from the command line, and also
|
||||
# from the environment for the first two.
|
||||
SPHINXOPTS ?=
|
||||
SPHINXBUILD ?= sphinx-build
|
||||
SOURCEDIR = .
|
||||
BUILDDIR = ../dist/docs
|
||||
|
||||
# Put it first so that "make" without argument is like "make help".
|
||||
help:
|
||||
@$(SPHINXBUILD) -M help "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O)
|
||||
|
||||
.PHONY: help Makefile
|
||||
|
||||
# Catch-all target: route all unknown targets to Sphinx using the new
|
||||
# "make mode" option. $(O) is meant as a shortcut for $(SPHINXOPTS).
|
||||
%: Makefile
|
||||
@$(SPHINXBUILD) -M $@ "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O)
|
0
docs/_static/.gitkeep
vendored
Normal file
0
docs/_static/.gitkeep
vendored
Normal file
40
docs/_toc.rst
Normal file
40
docs/_toc.rst
Normal file
@ -0,0 +1,40 @@
|
||||
Sitemap
|
||||
=======
|
||||
|
||||
.. toctree::
|
||||
:maxdepth: 1
|
||||
:caption: Basics
|
||||
|
||||
pages/install
|
||||
pages/guide
|
||||
|
||||
.. toctree::
|
||||
:maxdepth: 1
|
||||
:caption: Advanced Usage
|
||||
|
||||
pages/config
|
||||
pages/examples
|
||||
|
||||
.. toctree::
|
||||
:maxdepth: 1
|
||||
:caption: Developers
|
||||
|
||||
pages/developer
|
||||
pages/api
|
||||
|
||||
.. toctree::
|
||||
:maxdepth: 1
|
||||
:caption: Project Information
|
||||
|
||||
pages/changelog
|
||||
pages/license
|
||||
pages/issues
|
||||
pages/contributing
|
||||
|
||||
.. toctree::
|
||||
:maxdepth: 1
|
||||
:caption: Meta
|
||||
|
||||
genindex
|
||||
🌍 Project Page <https://git.codebau.dev/pytest-plugins/pytest-csv-params>
|
||||
🌍 juergen.rocks <https://juergen.rocks/>
|
112
docs/conf.py
Normal file
112
docs/conf.py
Normal file
@ -0,0 +1,112 @@
|
||||
# pylint: skip-file
|
||||
# mypy: ignore-errors
|
||||
|
||||
# Configuration file for the Sphinx documentation builder.
|
||||
#
|
||||
# For the full list of built-in configuration values, see the documentation:
|
||||
# https://www.sphinx-doc.org/en/master/usage/configuration.html
|
||||
|
||||
import sys
|
||||
from os.path import abspath, dirname, join
|
||||
|
||||
import tomli
|
||||
|
||||
sys.path.insert(0, abspath(join(dirname(__file__), "..")))
|
||||
# sys.path.insert(0, abspath(join(dirname(__file__), "..", "tests")))
|
||||
# sys.path.insert(0, abspath(join(dirname(__file__), "..", "_ptcsvp")))
|
||||
# sys.path.insert(0, abspath(join(dirname(__file__), "..", "pytest_csv_params")))
|
||||
|
||||
# -- Project information -----------------------------------------------------
|
||||
# https://www.sphinx-doc.org/en/master/usage/configuration.html#project-information
|
||||
|
||||
project = "pytest-csv-params"
|
||||
copyright = "2022, Jürgen Edelbluth"
|
||||
author = "Jürgen Edelbluth"
|
||||
# release = "0.0.0"
|
||||
|
||||
with open(abspath(join(dirname(__file__), "..", "pyproject.toml")), "rt", encoding="utf-8") as pyproject_toml:
|
||||
toml_data = tomli.loads(pyproject_toml.read())
|
||||
release = toml_data["tool"]["poetry"]["version"]
|
||||
|
||||
# -- General configuration ---------------------------------------------------
|
||||
# https://www.sphinx-doc.org/en/master/usage/configuration.html#general-configuration
|
||||
|
||||
extensions = [
|
||||
"sphinx.ext.intersphinx",
|
||||
"sphinx.ext.duration",
|
||||
"sphinx.ext.doctest",
|
||||
"sphinx.ext.autodoc",
|
||||
"sphinx.ext.autosummary",
|
||||
"sphinx.ext.napoleon",
|
||||
"sphinx_autodoc_typehints",
|
||||
"myst_parser",
|
||||
]
|
||||
|
||||
intersphinx_mapping = {
|
||||
"python": ("https://docs.python.org/3", (None, "python-inv.txt")),
|
||||
"pytest": ("https://docs.pytest.org/en/7.1.x/", (None, "pytest-inv.txt")),
|
||||
}
|
||||
|
||||
templates_path = ["_templates"]
|
||||
exclude_patterns = []
|
||||
|
||||
source_suffix = {
|
||||
".rst": "restructuredtext",
|
||||
".txt": "markdown",
|
||||
".md": "markdown",
|
||||
}
|
||||
|
||||
# -- Options for HTML output -------------------------------------------------
|
||||
# https://www.sphinx-doc.org/en/master/usage/configuration.html#options-for-html-output
|
||||
|
||||
html_theme = "sphinx_material"
|
||||
html_static_path = ["_static"]
|
||||
|
||||
html_title = "pytest-csv-params Documentation"
|
||||
html_short_title = "pytest-csv-params"
|
||||
|
||||
html_logo = "icon/pytest-csv-params.png"
|
||||
|
||||
pygments_style = "emacs"
|
||||
|
||||
# Material theme options (see theme.conf for more information)
|
||||
html_theme_options = {
|
||||
# Set the name of the project to appear in the navigation.
|
||||
"nav_title": "Pytest CSV Params Plugin",
|
||||
# Set you GA account ID to enable tracking
|
||||
"google_analytics_account": None,
|
||||
# Specify a base_url used to generate sitemap.xml. If not
|
||||
# specified, then no sitemap will be built.
|
||||
"base_url": None,
|
||||
# Set the color and the accent color
|
||||
"color_primary": "teal",
|
||||
"color_accent": "deep-orange",
|
||||
"repo_url": "https://git.codebau.dev/pytest-plugins/pytest-csv-params",
|
||||
"repo_name": "git.codebau.dev",
|
||||
"repo_type": None,
|
||||
# Visible levels of the global TOC; -1 means unlimited
|
||||
"globaltoc_depth": 1,
|
||||
# If False, expand all TOC entries
|
||||
"globaltoc_collapse": True,
|
||||
# If True, show hidden TOC entries
|
||||
"globaltoc_includehidden": False,
|
||||
"css_minify": True,
|
||||
"html_minify": True,
|
||||
"master_doc": True,
|
||||
}
|
||||
|
||||
myst_enable_extensions = [
|
||||
"colon_fence",
|
||||
]
|
||||
|
||||
html_sidebars = {
|
||||
"**": [
|
||||
"logo-text.html",
|
||||
"globaltoc.html",
|
||||
"localtoc.html",
|
||||
"searchbox.html",
|
||||
]
|
||||
}
|
||||
|
||||
autodoc_typehints_format = "fully-qualified"
|
||||
autodoc_preserve_defaults = True
|
2
docs/genindex.rst
Normal file
2
docs/genindex.rst
Normal file
@ -0,0 +1,2 @@
|
||||
Index
|
||||
=====
|
BIN
docs/icon/pytest-csv-params.png
Normal file
BIN
docs/icon/pytest-csv-params.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 40 KiB |
38
docs/index.md
Normal file
38
docs/index.md
Normal file
@ -0,0 +1,38 @@
|
||||
```{image} icon/pytest-csv-params.png
|
||||
:alt: Logo, shows a data table with an arrow to a test tube
|
||||
:class: bg-primary
|
||||
:width: 256px
|
||||
:align: center
|
||||
```
|
||||
|
||||
----
|
||||
|
||||
# Data-driven test parametrization für pytest with CSV files
|
||||
|
||||
[![PyPI - Downloads](https://img.shields.io/pypi/dw/pytest-csv-params?label=PyPI%20downloads&style=for-the-badge)](https://pypi.org/project/pytest-csv-params/)
|
||||
[![PyPI - Version](https://img.shields.io/pypi/v/pytest-csv-params?label=PyPI%20version&style=for-the-badge)](https://pypi.org/project/pytest-csv-params/)
|
||||
[![PyPI - Status](https://img.shields.io/pypi/status/pytest-csv-params?label=PyPI%20status&style=for-the-badge)](https://pypi.org/project/pytest-csv-params/)
|
||||
[![PyPI - Format](https://img.shields.io/pypi/format/pytest-csv-params?label=PyPI%20format&style=for-the-badge)](https://pypi.org/project/pytest-csv-params/)
|
||||
|
||||
This pytest plugin allows you to parametrize your pytest tests by CSV files. Manage your test data independently of
|
||||
your tests. This site guides you through [installation](pages/install) and [usage](pages/guide).
|
||||
|
||||
The plugin is [open source](https://git.codebau.dev/pytest-plugins/pytest-csv-params) and
|
||||
[released under MIT license](pages/license).
|
||||
It is listed in the [Python Package Index (PyPI)](https://pypi.org/project/pytest-csv-params/).
|
||||
|
||||
Your feedback, bug reports, improvements are appreciated! Get in touch via e-mail: `csv_params`@`jued`.`de`. Together
|
||||
we'll find a way for your contribution.
|
||||
|
||||
----
|
||||
|
||||
```{eval-rst}
|
||||
.. include:: _toc.rst
|
||||
```
|
||||
|
||||
----
|
||||
|
||||
```{note}
|
||||
This project is not affiliated with the pytest project, but heavily relies on it. Visit [pytest.org](https://pytest.org)
|
||||
for more information about this great testing framework.
|
||||
```
|
35
docs/make.bat
Normal file
35
docs/make.bat
Normal file
@ -0,0 +1,35 @@
|
||||
@ECHO OFF
|
||||
|
||||
pushd %~dp0
|
||||
|
||||
REM Command file for Sphinx documentation
|
||||
|
||||
if "%SPHINXBUILD%" == "" (
|
||||
set SPHINXBUILD=sphinx-build
|
||||
)
|
||||
set SOURCEDIR=.
|
||||
set BUILDDIR=..\dist\docs
|
||||
|
||||
%SPHINXBUILD% >NUL 2>NUL
|
||||
if errorlevel 9009 (
|
||||
echo.
|
||||
echo.The 'sphinx-build' command was not found. Make sure you have Sphinx
|
||||
echo.installed, then set the SPHINXBUILD environment variable to point
|
||||
echo.to the full path of the 'sphinx-build' executable. Alternatively you
|
||||
echo.may add the Sphinx directory to PATH.
|
||||
echo.
|
||||
echo.If you don't have Sphinx installed, grab it from
|
||||
echo.https://www.sphinx-doc.org/
|
||||
exit /b 1
|
||||
)
|
||||
|
||||
if "%1" == "" goto help
|
||||
|
||||
%SPHINXBUILD% -M %1 %SOURCEDIR% %BUILDDIR% %SPHINXOPTS% %O%
|
||||
goto end
|
||||
|
||||
:help
|
||||
%SPHINXBUILD% -M help %SOURCEDIR% %BUILDDIR% %SPHINXOPTS% %O%
|
||||
|
||||
:end
|
||||
popd
|
67
docs/pages/api-ref/private.md
Normal file
67
docs/pages/api-ref/private.md
Normal file
@ -0,0 +1,67 @@
|
||||
# Private Elements
|
||||
|
||||
## Command Line Arguments
|
||||
|
||||
**Module:** `_ptcsvp.cmdline`
|
||||
|
||||
```{eval-rst}
|
||||
.. automodule:: _ptcsvp.cmdline
|
||||
:members:
|
||||
:private-members:
|
||||
:undoc-members:
|
||||
```
|
||||
|
||||
## Plugin Configuration
|
||||
|
||||
**Module:** `_ptcsvp.configure`
|
||||
|
||||
```{eval-rst}
|
||||
.. automodule:: _ptcsvp.configure
|
||||
:members:
|
||||
:private-members:
|
||||
:undoc-members:
|
||||
```
|
||||
|
||||
## The Parametrization
|
||||
|
||||
**Module:** `_ptcsvp.parametrize`
|
||||
|
||||
```{eval-rst}
|
||||
.. automodule:: _ptcsvp.parametrize
|
||||
:members:
|
||||
:private-members:
|
||||
:undoc-members:
|
||||
```
|
||||
|
||||
## The Plugin Class
|
||||
|
||||
**Module:** `_ptcsvp.plugin`
|
||||
|
||||
```{eval-rst}
|
||||
.. automodule:: _ptcsvp.plugin
|
||||
:members:
|
||||
:private-members:
|
||||
:undoc-members:
|
||||
```
|
||||
|
||||
## The Variable Name Validation
|
||||
|
||||
**Module:** `_ptcsvp.varname`
|
||||
|
||||
```{eval-rst}
|
||||
.. automodule:: _ptcsvp.varname
|
||||
:members:
|
||||
:private-members:
|
||||
:undoc-members:
|
||||
```
|
||||
|
||||
## The Version Checks
|
||||
|
||||
**Module:** `_ptcsvp.version`
|
||||
|
||||
```{eval-rst}
|
||||
.. automodule:: _ptcsvp.version
|
||||
:members:
|
||||
:private-members:
|
||||
:undoc-members:
|
||||
```
|
49
docs/pages/api-ref/public.md
Normal file
49
docs/pages/api-ref/public.md
Normal file
@ -0,0 +1,49 @@
|
||||
# Public Elements
|
||||
|
||||
## The Decorator
|
||||
|
||||
**Module:** `pytest_csv_params.decorator`
|
||||
|
||||
```{eval-rst}
|
||||
.. automodule:: pytest_csv_params.decorator
|
||||
:members:
|
||||
:undoc-members:
|
||||
```
|
||||
|
||||
## The CSV Dialect
|
||||
|
||||
**Module:** `pytest_csv_params.dialect`
|
||||
|
||||
```{eval-rst}
|
||||
.. automodule:: pytest_csv_params.dialect
|
||||
:members:
|
||||
```
|
||||
|
||||
## The Exceptions
|
||||
|
||||
**Module:** `pytest_csv_params.exception`
|
||||
|
||||
```{eval-rst}
|
||||
.. automodule:: pytest_csv_params.exception
|
||||
:members:
|
||||
```
|
||||
|
||||
## Plugin Code
|
||||
|
||||
**Module:** `pytest_csv_params.plugin`
|
||||
|
||||
```{eval-rst}
|
||||
.. automodule:: pytest_csv_params.plugin
|
||||
:members:
|
||||
:undoc-members:
|
||||
```
|
||||
|
||||
## Types
|
||||
|
||||
**Module:** `pytest_csv_params.types`
|
||||
|
||||
```{eval-rst}
|
||||
.. automodule:: pytest_csv_params.types
|
||||
:members:
|
||||
:undoc-members:
|
||||
```
|
127
docs/pages/api-ref/tests.md
Normal file
127
docs/pages/api-ref/tests.md
Normal file
@ -0,0 +1,127 @@
|
||||
# Tests
|
||||
|
||||
This page should give you an overview over all tests for this plugin.
|
||||
|
||||
## Global `conftest.py`
|
||||
|
||||
**Module:** `tests.conftest`
|
||||
|
||||
```{eval-rst}
|
||||
.. automodule:: tests.conftest
|
||||
:members:
|
||||
:private-members:
|
||||
:undoc-members:
|
||||
```
|
||||
|
||||
## Standard Tests
|
||||
|
||||
```{eval-rst}
|
||||
.. automodule:: tests.test_clean_headers
|
||||
:members:
|
||||
:private-members:
|
||||
:undoc-members:
|
||||
```
|
||||
|
||||
```{eval-rst}
|
||||
.. automodule:: tests.test_parametrize
|
||||
:members:
|
||||
:private-members:
|
||||
:undoc-members:
|
||||
```
|
||||
|
||||
```{eval-rst}
|
||||
.. automodule:: tests.test_read_csv
|
||||
:members:
|
||||
:private-members:
|
||||
:undoc-members:
|
||||
```
|
||||
|
||||
```{eval-rst}
|
||||
.. automodule:: tests.test_varname
|
||||
:members:
|
||||
:private-members:
|
||||
:undoc-members:
|
||||
```
|
||||
|
||||
```{eval-rst}
|
||||
.. automodule:: tests.test_version_check
|
||||
:members:
|
||||
:private-members:
|
||||
:undoc-members:
|
||||
```
|
||||
|
||||
## Plugin Tests
|
||||
|
||||
These tests test the plugin code by inserting the plugin into a test pytest instance.
|
||||
|
||||
### Plugin `conftest.py`
|
||||
|
||||
**Module:** `tests.plugin.conftest`
|
||||
|
||||
```{eval-rst}
|
||||
.. automodule:: tests.plugin.conftest
|
||||
:members:
|
||||
:private-members:
|
||||
:undoc-members:
|
||||
```
|
||||
|
||||
### Tests
|
||||
|
||||
```{eval-rst}
|
||||
.. automodule:: tests.plugin.test_cmd_line
|
||||
:members:
|
||||
:private-members:
|
||||
:undoc-members:
|
||||
```
|
||||
|
||||
```{eval-rst}
|
||||
.. automodule:: tests.plugin.test_plugin
|
||||
:members:
|
||||
:private-members:
|
||||
:undoc-members:
|
||||
```
|
||||
|
||||
## POC Tests
|
||||
|
||||
### POC `conftest.py`
|
||||
|
||||
**Module:** `tests.poc.conftest`
|
||||
|
||||
```{eval-rst}
|
||||
.. automodule:: tests.poc.conftest
|
||||
:members:
|
||||
:private-members:
|
||||
:undoc-members:
|
||||
```
|
||||
|
||||
### Tests
|
||||
|
||||
```{eval-rst}
|
||||
.. automodule:: tests.poc.test_parametrize_with_generator
|
||||
:members:
|
||||
:private-members:
|
||||
:undoc-members:
|
||||
```
|
||||
|
||||
## Examples
|
||||
|
||||
```{eval-rst}
|
||||
.. automodule:: tests.test_docs_example
|
||||
:members:
|
||||
:private-members:
|
||||
:undoc-members:
|
||||
```
|
||||
|
||||
```{eval-rst}
|
||||
.. automodule:: tests.test_blog_example
|
||||
:members:
|
||||
:private-members:
|
||||
:undoc-members:
|
||||
```
|
||||
|
||||
```{eval-rst}
|
||||
.. automodule:: tests.test_complex_example
|
||||
:members:
|
||||
:private-members:
|
||||
:undoc-members:
|
||||
```
|
11
docs/pages/api.md
Normal file
11
docs/pages/api.md
Normal file
@ -0,0 +1,11 @@
|
||||
# API Reference
|
||||
|
||||
The following documents are generated from the source code documentation.
|
||||
|
||||
```{toctree}
|
||||
:maxdepth: 2
|
||||
|
||||
api-ref/public
|
||||
api-ref/private
|
||||
api-ref/tests
|
||||
```
|
103
docs/pages/changelog.md
Normal file
103
docs/pages/changelog.md
Normal file
@ -0,0 +1,103 @@
|
||||
# Changelog
|
||||
|
||||
## Version 0.4.0
|
||||
|
||||
<u>Breaking Changes:</u> ✓ None
|
||||
|
||||
<u>Changes:</u>
|
||||
|
||||
- Structured Documentation (see source folder `docs/`), it is an important milestone to version 1.0; published under:
|
||||
[docs.codebau.dev/pytest-plugins/pytest-csv-params](https://docs.codebau.dev/pytest-plugins/pytest-csv-params)
|
||||
- Documentation widely extended with a lot of extra information
|
||||
- A more detailed changelog as part of this documentation
|
||||
- Some source code / API documentation
|
||||
- `README.md` reduced in favor of the structured documentation
|
||||
|
||||
[Downloads](https://git.codebau.dev/pytest-plugins/pytest-csv-params/releases/tag/v0.4.0) |
|
||||
[Technical Changelog](https://git.codebau.dev/pytest-plugins/pytest-csv-params/compare/v0.3.0...v0.4.0)
|
||||
|
||||
## Version 0.3.0
|
||||
|
||||
<u>Breaking Changes:</u>
|
||||
|
||||
- Column names are now tested for reserved names. If you used reserved names in the past, this might break your tests,
|
||||
despite the fact, that you are greater trouble already.
|
||||
|
||||
<u>Changes:</u>
|
||||
|
||||
- Much better handling of column names (headers) in CSV files:
|
||||
- Invalid characters are replaced by a `_`
|
||||
- Names are checked if they are reserved keywords or builtin names
|
||||
- A new parameter `header_renames` to the decorator `@csv_params` allows you to bring your CSV column names to clean
|
||||
variable names
|
||||
- See `README.md` for further details
|
||||
|
||||
[Downloads](https://git.codebau.dev/pytest-plugins/pytest-csv-params/releases/tag/v0.3.0) |
|
||||
[Technical Changelog](https://git.codebau.dev/pytest-plugins/pytest-csv-params/compare/v0.2.2...v0.3.0)
|
||||
|
||||
## Version 0.2.2
|
||||
|
||||
<u>Breaking Changes:</u> ✓ None
|
||||
|
||||
<u>Changes:</u>
|
||||
|
||||
- Library updates
|
||||
- For Developers: Added a few extra tests for base functionality of pytest that is used in this plugin.
|
||||
|
||||
[Downloads](https://git.codebau.dev/pytest-plugins/pytest-csv-params/releases/tag/v0.2.2) |
|
||||
[Technical Changelog](https://git.codebau.dev/pytest-plugins/pytest-csv-params/compare/v0.2.0...v0.2.2)
|
||||
|
||||
## Version 0.2.0
|
||||
|
||||
<u>Breaking Changes:</u>
|
||||
|
||||
- The order of the parameters for the decorator `@csv_params` changed to realize the new shorthand form of the decorator
|
||||
(see below). If you used the decorator with keyword parameters only (like it is written in the documentation), you are
|
||||
fine.
|
||||
|
||||
<u>Changes</u>
|
||||
|
||||
- New shorthand form for the decorator `@csv_params`. This is very handy when you have CSV files with no ID column and
|
||||
column names that match the test functions parameters names. Together with the command line parameter introduced in
|
||||
version 0.1.0 you can create very short decorators. See `README.md` for details.
|
||||
- For Developers: Mypy has been added to the test chain for typing analysis
|
||||
|
||||
[Downloads](https://git.codebau.dev/pytest-plugins/pytest-csv-params/releases/tag/v0.2.0) |
|
||||
[Technical Changelog](https://git.codebau.dev/pytest-plugins/pytest-csv-params/compare/v0.1.0...v0.2.0)
|
||||
|
||||
## Version 0.1.0
|
||||
|
||||
<u>Breaking Changes:</u> ✓ None
|
||||
|
||||
<u>Changes:</u>
|
||||
|
||||
- A new command line argument `--csv-params-base-dir` allows you to set a base dir for all relative CSV files. This is
|
||||
great when you have a central storage for your test data. See `README.md` for more details.
|
||||
- Some documentation fixes
|
||||
- Some changes to the tox configuration in order to report coverage correctly
|
||||
|
||||
[Downloads](https://git.codebau.dev/pytest-plugins/pytest-csv-params/releases/tag/v0.1.0) |
|
||||
[Technical Changelog](https://git.codebau.dev/pytest-plugins/pytest-csv-params/compare/v0.0.4...v0.1.0)
|
||||
|
||||
## Version 0.0.4
|
||||
|
||||
<u>Breaking Changes:</u> ✓ None
|
||||
|
||||
<u>Changes:</u>
|
||||
|
||||
- Minor documentation bugfixes
|
||||
|
||||
[Downloads](https://git.codebau.dev/pytest-plugins/pytest-csv-params/releases/tag/v0.0.4) |
|
||||
[Technical Changelog](https://git.codebau.dev/pytest-plugins/pytest-csv-params/compare/v0.0.3...v0.0.4)
|
||||
|
||||
## Version 0.0.3
|
||||
|
||||
<u>Breaking Changes:</u> ✓ None
|
||||
|
||||
<u>Changes:</u>
|
||||
|
||||
- Initial Public Release
|
||||
- Delivered the `@csv_params` decorator
|
||||
|
||||
[Downloads](https://git.codebau.dev/pytest-plugins/pytest-csv-params/releases/tag/v0.0.3) |
|
||||
[Technical Changelog](https://git.codebau.dev/pytest-plugins/pytest-csv-params/compare/v0.0.2...v0.0.3)
|
254
docs/pages/config.md
Normal file
254
docs/pages/config.md
Normal file
@ -0,0 +1,254 @@
|
||||
# Configuration
|
||||
|
||||
## Decorator Parameters
|
||||
|
||||
These are the parameters for the decorator {meth}`pytest_csv_params.decorator.csv_params`.
|
||||
|
||||
### Overview
|
||||
|
||||
| Parameter | Type | Description | Example |
|
||||
|------------------|--------------------------|----------------------------------------------------------------------------------------------------------------------------------------|------------------------------------------------------------------------------------------------|
|
||||
| `data_file` | `str` | The CSV file to use, relative or absolute path | `"/var/testdata/test1.csv"` |
|
||||
| `base_dir` | `str` (optional) | Directory to look up relative CSV files (see `data_file`); overrides the command line argument | `join(dirname(__file__), "assets")` |
|
||||
| `id_col` | `str` (optional) | Column name of the CSV that contains test case IDs | `"ID#"` |
|
||||
| `dialect` | `csv.Dialect` (optional) | CSV Dialect definition (see [Python CSV Documentation](https://docs.python.org/3/library/csv.html#dialects-and-formatting-parameters)) | `csv.excel_tab` |
|
||||
| `data_casts` | `dict` (optional) | Cast Methods for the CSV Data (see "Data Casting" below) | `{ "a": int, "b": float }` |
|
||||
| `header_renames` | `dict` (optional) | Replace headers from the CSV file, so that they can be used as parameters for the test function (since 0.3.0) | `{ "Annual Amount of Bananas": "banana_count", "Cherry export price": "cherry_export_price" }` |
|
||||
|
||||
### Detailed Description
|
||||
|
||||
#### `data_file`
|
||||
|
||||
This points to the CSV file to load for this test. You can use relative or absolute paths. If you use a relative path
|
||||
and a `base_dir`, the `base_dir` is prepended to the `data_file`.
|
||||
|
||||
````{admonition} Hint
|
||||
It's a good idea to put your CSV data files in a `test-assets` folder on the same level than your `test_something.py`
|
||||
file.
|
||||
|
||||
Example Layout:
|
||||
|
||||
```text
|
||||
tests/
|
||||
+- test-assets/
|
||||
| +- case1.csv
|
||||
| +- case2.csv
|
||||
+- test_case1.py
|
||||
+- test_case2.py
|
||||
```
|
||||
|
||||
Now use this for `data_file` and `base_dir` (in one of the `test_caseX.py`):
|
||||
|
||||
```python
|
||||
from os.path import dirname, join
|
||||
from pytest_csv_params.decorator import csv_params
|
||||
|
||||
@csv_params(data_file="case1.csv", base_dir=join(dirname(__file__), "test-assets"))
|
||||
def test_case1():
|
||||
...
|
||||
```
|
||||
````
|
||||
|
||||
#### `base_dir`
|
||||
|
||||
This is an optional parameter. Set it to the directory where the CSV file from the `data_file` parameter should be
|
||||
looked up. If not `None` (which is the default value), the value will be prepended to the `data_file` value, as long as
|
||||
`data_file` is not an absolute path.
|
||||
|
||||
See `--csv-params-base-dir` command line argument below also.
|
||||
|
||||
```{warning}
|
||||
Setting `base_dir` to something that is not `None` overrides anything that is set by the `--csv-params-base-dir`
|
||||
command line argument.
|
||||
```
|
||||
|
||||
#### `id_col`
|
||||
|
||||
Name the column that contains the test case IDs. If `None` (which is the default value), no test case IDs will be
|
||||
generated. In this case, pytest will create its own IDs based on the parameters for the test. The column name does not
|
||||
need to be valid variable/argument name.
|
||||
|
||||
Example:
|
||||
|
||||
```text
|
||||
"Test Case ID#", "val_a", "val_b"
|
||||
"test-12 / 4", "1234", "4321"
|
||||
"test-13 / 7", "3210", "0123"
|
||||
"test-14 / 9", "5432", "2345"
|
||||
```
|
||||
|
||||
The test case ID is in the column "Test Case ID#". You'd configure it like this:
|
||||
|
||||
```python
|
||||
from os.path import dirname, join
|
||||
from pytest_csv_params.decorator import csv_params
|
||||
|
||||
@csv_params(data_file=join(dirname(__file__), "test-assets", "case1.csv"), id_col="Test Case ID#")
|
||||
def test_case1(param_1: str, param_2: str) -> None:
|
||||
...
|
||||
```
|
||||
|
||||
#### `dialect`
|
||||
|
||||
Set the CSV dialect, it must be of the type {class}`csv.Dialect`. A dialect defines how a CSV file looks like.
|
||||
|
||||
The default dialect is {class}`pytest_csv_params.dialect.CsvParamsDefaultDialect`.
|
||||
|
||||
A dialect consists of the following settings:
|
||||
|
||||
| Setting | Default value in {class}`~pytest_csv_params.dialect.CsvParamsDefaultDialect` |
|
||||
|---------------------------------------|------------------------------------------------------------------------------|
|
||||
| {attr}`~csv.Dialect.delimiter` | `","` |
|
||||
| {attr}`~csv.Dialect.doublequote` | `True` |
|
||||
| {attr}`~csv.Dialect.escapechar` | `None` |
|
||||
| {attr}`~csv.Dialect.lineterminator` | `"\r\n"` |
|
||||
| {attr}`~csv.Dialect.quotechar` | `'"'` |
|
||||
| {attr}`~csv.Dialect.quoting` | {data}`csv.QUOTE_ALL` |
|
||||
| {attr}`~csv.Dialect.skipinitialspace` | `True` |
|
||||
| {attr}`~csv.Dialect.strict` | `True` |
|
||||
|
||||
See [Usage Examples](examples) to learn how to create your own decorator that would always use your own specific CSV
|
||||
file dialect.
|
||||
|
||||
```{note}
|
||||
Regardless of the format parameters you are defining, all values from the CSV file are read as `str`. You may need to
|
||||
convert them into other types. This is where `data_casts` are for.
|
||||
```
|
||||
|
||||
#### `data_casts`
|
||||
|
||||
This dictionary allows you to setup methods to convert the string values from the CSV files into types or formats
|
||||
required for test execution.
|
||||
|
||||
```{admonition} Rule of thumb
|
||||
1. You can use any method that accepts a single `str` parameter. It can return anything you need.
|
||||
2. If you need to test your test code, you should prefer conversion methods over conversion lambdas.
|
||||
```
|
||||
|
||||
Example:
|
||||
|
||||
```text
|
||||
"Test Case ID#", "val_a", "val_b", "val_c", "val_d", "val_e"
|
||||
"test-12 / 4", "2.022", "152", "1 x 3", "abcd", "flox"
|
||||
"test-13 / 7", "3.125", "300", "2 x 4", "defg", "trox"
|
||||
"test-14 / 9", "4.145", "150", "3x6x9", "hijk", "bank"
|
||||
```
|
||||
|
||||
- The values of column "Test Case ID#" do not need any conversion. The column will serve as `id_col`.
|
||||
- The values of column "val_a" should be converted into `float`. Since `float` is also a method, it can be used
|
||||
directly.
|
||||
- The values of column "val_b" should be converted into `int`. Since `int` is also a method, it can be used directly.
|
||||
- The values of column "val_c" must be converted a bit more complex. We'll use a `lambda` for that.
|
||||
- The values of column "val_d" don't need to be converted. They are `str`.
|
||||
- The values of column "val_e" will be converted with a helper method (`convert_val_e`).
|
||||
|
||||
Implementation of this example:
|
||||
|
||||
```python
|
||||
from typing import List, Optional, Tuple
|
||||
from pytest_csv_params.decorator import csv_params
|
||||
|
||||
def convert_val_e(value: str) -> Tuple[bool, Optional[str]]:
|
||||
str_val = None
|
||||
bool_val = value.endswith("ox")
|
||||
if bool_val:
|
||||
str_val = value[:2]
|
||||
return bool_val, str_val
|
||||
|
||||
@csv_params(
|
||||
data_file="test1.csv",
|
||||
id_col="Test Case ID#",
|
||||
data_casts={
|
||||
"val_a": float,
|
||||
"val_b": int,
|
||||
"val_c": lambda x: list(map(lambda y: y.strip(), x.split("x"))),
|
||||
"val_e": convert_val_e,
|
||||
},
|
||||
)
|
||||
def test_something(val_a: float, val_b: int, val_c: List[int], val_d: str, val_e: Tuple[bool, Optional[str]]) -> None:
|
||||
...
|
||||
```
|
||||
|
||||
```{note}
|
||||
In this example, the columns were named as valid argument/parameter names. So there's no need for `header_renames` here.
|
||||
```
|
||||
|
||||
#### `header_renames`
|
||||
|
||||
This dictionary allows to rename the column headers into valid argument names for your test methods. The plugin will try
|
||||
to rename invalid header names by replacing invalid chars with underscores, but this might not result in well-formed and
|
||||
readable names.
|
||||
|
||||
Example:
|
||||
|
||||
```text
|
||||
"Test Case ID#", "Flux Compensator Setting", "Power Level"
|
||||
"101 / 885 / 31", "1-1-2-1-2-7-5-3-4-9/7", "100 %"
|
||||
"109 / 995 / 21", "3-2-2-2-6-4-2-2-1-2/8", "15 %"
|
||||
"658 / 555 / 54", "3-2-3-4-5-6-7-3-2-3/2", "25 %"
|
||||
```
|
||||
|
||||
Configuration of the decorator:
|
||||
|
||||
```python
|
||||
from pytest_csv_params.decorator import csv_params
|
||||
|
||||
@csv_params(
|
||||
data_file="test.csv",
|
||||
id_col="Test Case ID#",
|
||||
header_renames={
|
||||
"Flux Compensator Setting": "flux_setting",
|
||||
"Power Level": "power_level",
|
||||
},
|
||||
)
|
||||
def test_something_else(fux_setting: str, power_level: str) -> None:
|
||||
...
|
||||
```
|
||||
|
||||
```{warning}
|
||||
`data_casts` dictionary keys must match the renamed column names!
|
||||
```
|
||||
|
||||
## Command Line Arguments
|
||||
|
||||
These are the command line arguments for the pytest run.
|
||||
|
||||
### Overview
|
||||
|
||||
| Argument | Required | Description | Example |
|
||||
|-------------------------|---------------|----------------------------------------------------------------------|----------------------------------------------|
|
||||
| `--csv-params-base-dir` | no (optional) | Define a base dir for all relative-path CSV data files (since 0.1.0) | `pytest --csv-params-base-dir /var/testdata` |
|
||||
|
||||
### Detailed Description
|
||||
|
||||
#### `--csv-params-base-dir`
|
||||
|
||||
This is a convenience command line argument. It allows you to set a base directory for all your CSV parametrized test
|
||||
cases. If you use relative `data_file`s, this can be automatically prepended. You can still override this setting per
|
||||
test by using the `base_dir` configuration.
|
||||
|
||||
## How a CSV file is found
|
||||
|
||||
```text
|
||||
+-----------------------------------+ /-----------------------------------\
|
||||
| data_dir is absolute path? | --- yes --- | use this path |
|
||||
+-----------------------------------+ \-----------------------------------/
|
||||
|
|
||||
no
|
||||
|
|
||||
+-----------------------------------+ /-----------------------------------\
|
||||
| is a base_dir set on the test? | --- yes --- | prepend base_dir to data_file |
|
||||
+-----------------------------------+ \-----------------------------------/
|
||||
|
|
||||
no
|
||||
|
|
||||
+-----------------------------------+ /-----------------------------------\
|
||||
| is command line argument given? | --- yes --- | prepend arg value to data_file |
|
||||
+-----------------------------------+ \-----------------------------------/
|
||||
|
|
||||
no
|
||||
|
|
||||
/-----------------------------------\
|
||||
| use data_file as relative path |
|
||||
\-----------------------------------/
|
||||
```
|
6
docs/pages/contributing.md
Normal file
6
docs/pages/contributing.md
Normal file
@ -0,0 +1,6 @@
|
||||
# Contributing
|
||||
|
||||
```{eval-rst}
|
||||
.. include:: ../../CONTRIBUTING.md
|
||||
:parser: myst_parser.sphinx_
|
||||
```
|
195
docs/pages/developer.md
Normal file
195
docs/pages/developer.md
Normal file
@ -0,0 +1,195 @@
|
||||
# Developer Guide
|
||||
|
||||
If you want to develop for / with the Pytest CSV Params Plugin, consider to clone the repository:
|
||||
|
||||
```bash
|
||||
git clone https://git.codebau.dev/pytest-plugins/pytest-csv-params.git
|
||||
```
|
||||
|
||||
You need **Python 3.8** or newer.
|
||||
|
||||
The project's dependencies and building are managed by `poetry`. Please follow the instructions from
|
||||
[python-poetry.org](https://python-poetry.org/) to install `poetry` on your system.
|
||||
|
||||
Install all the dependencies, including the development dependencies:
|
||||
|
||||
```bash
|
||||
poetry install
|
||||
```
|
||||
|
||||
## Commit Signing
|
||||
|
||||
Commit signing is mandatory for all commits for the `main` branch. Please make sure, your public key is set up and
|
||||
registered with `git.codebau.dev`.
|
||||
|
||||
## Testing
|
||||
|
||||
Tests are implemented with `pytest`. You find them in the `tests` folder. Besides unit and integration tests, some other
|
||||
checks are executed by `pytest` plugins:
|
||||
|
||||
- **`pytest-black`:** This plugin checks code formatting with [`black`](https://github.com/psf/black). If tests fail,
|
||||
try `poetry run black .` from the project root to fix formatting issues. Configuration: `pyproject.toml`, section
|
||||
`[tool.black]`.
|
||||
- **`pytest-isort`:** This plugin checks import sorting with [`isort`](https://github.com/PyCQA/isort). If tests fail,
|
||||
try `poetry run isort .` from the project root to fix import sorting issues. Configuration: `pyproject.toml`, section
|
||||
`[tool.isort]`.
|
||||
- **`pytest-pylint`:** This plugin does a static code analysis with [`pylint`](https://github.com/PyCQA/pylint). The
|
||||
test configuration can be found in `.pylintrc` in the project root.
|
||||
- **`pytest-bandit`:** This plugin performs a static security analysis of the code with
|
||||
[`bandit`](https://github.com/PyCQA/bandit). The configuration is part of the `[tool.pytest.ini_options]` section in
|
||||
the `pyproject.toml`, config keys `bandit_*`.
|
||||
- **`pytest-mypy`:** This plugin uses [`mypy`](https://mypy.readthedocs.io/en/stable/) to perform typing checks against
|
||||
the code. The configuration can be found in the `pyproject.toml`, section `[tool.mypy]`.
|
||||
|
||||
Most plugins are enabled by the `addopts` switches, configured in the `pyproject.toml`, section
|
||||
`[tool.pytest.ini_options]`. Some plugins have extra configuration switches even there.
|
||||
|
||||
Additionally, the code coverage is measured by `pytest-cov` using [`coverage.py`](https://github.com/nedbat/coveragepy).
|
||||
A high coverage alone is not a very good metric, but it helps to find and fix coverage weaknesses. The configuration for
|
||||
coverage measurement is in the `pyproject.toml`, sections `[tool.coverage]`, `[tool.coverage.run]` and
|
||||
`[tool.coverage.report]`.
|
||||
|
||||
There are some other pytest plugins installed and used for tests:
|
||||
|
||||
- **`pytest-mock`:** Simplified mocking
|
||||
- **`pytest-clarity`:** Better output of assertion errors
|
||||
- **`pytest-order`:** Execute tests in a given order (used in {mod}`tests.poc.test_parametrize_with_generator`).
|
||||
|
||||
### Test runs with `pytest`
|
||||
|
||||
Just run all the tests with:
|
||||
|
||||
```bash
|
||||
poetry run pytest
|
||||
```
|
||||
|
||||
### Test runs with `tox`
|
||||
|
||||
`tox` is used to execute all tests under the different supported Python versions. Make sure you installed all relevant
|
||||
versions on your system, for example with [`pyenv`](https://github.com/pyenv/pyenv).
|
||||
|
||||
To execute them all, run:
|
||||
|
||||
```bash
|
||||
poetry run tox
|
||||
```
|
||||
|
||||
If you experience strange `tox` errors, try to recreate the `tox` environments:
|
||||
|
||||
```bash
|
||||
poetry run tox -r
|
||||
```
|
||||
|
||||
`tox` is configured in the `pyproject.toml`, section `[tool.tox]`.
|
||||
|
||||
```{admonition} No new or changed code without test
|
||||
If you add or change code, please make sure your changes are covered by meaningful tests.
|
||||
```
|
||||
|
||||
## Building
|
||||
|
||||
There are two different things to build from the source code: The **Wheel distribution package** from the Python code
|
||||
and the **documentation**.
|
||||
|
||||
### Code
|
||||
|
||||
The publishing is done managed with `poetry`. The complete build and deploy configuration takes place in the
|
||||
`pyproject.toml`. Besides the standard configuration in section `[tool.poetry]`, additional URLs are defined in section
|
||||
`[tool.poetry.urls]`. As a speciality for this plugin, an entry point is defined in section
|
||||
`[tool.poetry.plugins."pytest11"]`.
|
||||
|
||||
To build the packages, just run `poetry build` from the project root.
|
||||
|
||||
(build-docs)=
|
||||
### Docs
|
||||
|
||||
The docs are in the `docs` folder. There is a `conf.py` that contains all the settings. Documentation is managed by
|
||||
[`sphinx`](https://www.sphinx-doc.org/). There is a `make` file (`Makefile`) as well as a `make.bat`, they contain some
|
||||
configuration also.
|
||||
|
||||
The `serve.py` scripts starts a live reload server to preview the documentation.
|
||||
|
||||
To build the documentation, run `poetry run make html` (respectively `poetry run make.bat html` on Windows) from the
|
||||
`docs` directory.
|
||||
|
||||
## Publishing
|
||||
|
||||
```{warning}
|
||||
The following section is more a reference for project members. If you not belong to the project, you'll not be able to
|
||||
publish or update packages.
|
||||
|
||||
Maybe you find it helpful as a boiler plate for your own projects.
|
||||
```
|
||||
|
||||
### Increase Version
|
||||
|
||||
If not already done, increase the version in the `pyproject.toml`. This can be done manually, but `poetry` offers a
|
||||
helper for that:
|
||||
|
||||
| `poetry` command | Effect |
|
||||
|------------------------|----------------|
|
||||
| `poetry version patch` | increase patch |
|
||||
| `poetry version minor` | increase minor |
|
||||
| `poetry version major` | increase major |
|
||||
|
||||
### Complete Changelog
|
||||
|
||||
Update the `docs/pages/changelog.md` file with all relevant things happened since the last release. Set a compare link
|
||||
and a link to the release page. You can set them up even if the release does not exist at the moment.
|
||||
|
||||
Don't forget to commit now!
|
||||
|
||||
### Tag the release
|
||||
|
||||
Set a git tag in the format `vX.Y.Z` (with the leading `v`). Push all your commits and the tag now.
|
||||
|
||||
### PyPI
|
||||
|
||||
```{admonition} Poetry configuration for publishing
|
||||
If not already done, you need to setup `poetry` for publishing.
|
||||
|
||||
**1. Configuration for production PyPI**
|
||||
|
||||
- Get your token from [pypi.org](https://pypi.org/)
|
||||
- Set your token with `poetry config pypi-token.pypi pypi-YOUR_PROD_TOKEN`
|
||||
|
||||
**2. Configuration for test PyPI**
|
||||
|
||||
- Get your token from [test.pypi.org](https://test.pypi.org/)
|
||||
- Setup the test repo: `poetry config repositories.test.url https://test.pypi.org/legacy/`
|
||||
- Set your token with `poetry config pypi-token.test pypi-YOUR_TEST_TOKEN`
|
||||
|
||||
**3. Configuration for Codebau Package Repository**
|
||||
|
||||
- Get your token from [git.codebau.dev](https://git.codebau.dev/)
|
||||
- Setup the codebau repo:
|
||||
`poetry config repositories.codebau.url https://git.codebau.dev/api/packages/pytest-plugins/pypi`
|
||||
- Setup your token with `poetry config pypi-token.codebau YOUR_CODEBAU_TOKEN`
|
||||
```
|
||||
|
||||
#### Publish to test.pypi.org
|
||||
|
||||
It's a good practice to publish a new package to [test.pypi.org](https://test.pypi.org/) first.
|
||||
|
||||
```bash
|
||||
poetry publish --build -r test
|
||||
```
|
||||
|
||||
You can omit the `--build` param when you already built the package.
|
||||
|
||||
#### Publish to production pypi.org
|
||||
|
||||
```bash
|
||||
poetry publish --build
|
||||
```
|
||||
|
||||
#### Publish to git.codebau.dev Package Repository
|
||||
|
||||
```bash
|
||||
poetry publish --build -r codebau
|
||||
```
|
||||
|
||||
### Documentation
|
||||
|
||||
The documentation is automatically build from the `main` branch and published to `docs.codebau.dev`. If you want to
|
||||
build by yourself, see {ref}`Building / Docs <build-docs>`. You find the compiled docs under `dist/docs/html`.
|
25
docs/pages/examples.md
Normal file
25
docs/pages/examples.md
Normal file
@ -0,0 +1,25 @@
|
||||
# Usage Examples
|
||||
|
||||
## Build your own annotation
|
||||
|
||||
Using another CSV format? The same `data_casts` methods each time? There is only one name for a test ID column? You can
|
||||
easily build your own annotation. Just create a method that contains your common stuff:
|
||||
|
||||
```python
|
||||
from pytest_csv_params.decorator import csv_params
|
||||
|
||||
def my_csv_params(data_file: str, **kwargs):
|
||||
kwargs.setdefault("base_dir", "/var/test-data")
|
||||
kwargs.setdefault("id_col", "Test Case ID")
|
||||
kwargs.setdefault("header_renames", {
|
||||
"Order #": "order_number",
|
||||
"Price Total": "total_price",
|
||||
})
|
||||
kwargs.setdefault("data_casts", {
|
||||
"total_price": float,
|
||||
})
|
||||
return csv_params(data_file, **kwargs)
|
||||
```
|
||||
|
||||
When you now write a test, you can decorate it with `@my_csv_params("test-file-1.csv")`. You can override any of your
|
||||
default settings by just adding it as a keyword arg |