├── .github └── workflows │ └── python-package.yml ├── .gitignore ├── AUTHORS.md ├── CHANGELOG.md ├── CONTRIBUTING.md ├── INSTALL.md ├── LICENSE ├── README.md ├── datafixtures ├── __init__.py └── entry.py ├── pyproject.toml └── tests ├── datafixtures ├── fileone └── test_datafix.txt ├── sub ├── datafixtures │ └── test_sub └── test_submodule.py └── test_module.py /.github/workflows/python-package.yml: -------------------------------------------------------------------------------- 1 | name: Python package 2 | 3 | on: 4 | push: 5 | branches: [ master ] 6 | pull_request: 7 | branches: [ master ] 8 | workflow_dispatch: 9 | 10 | jobs: 11 | build: 12 | 13 | runs-on: ubuntu-latest 14 | strategy: 15 | fail-fast: false 16 | matrix: 17 | python-version: ["3.10", 3.11, 3.12, 3.13] 18 | 19 | steps: 20 | - uses: actions/checkout@v4 21 | - name: Set up Python ${{ matrix.python-version }} 22 | uses: actions/setup-python@v5 23 | with: 24 | python-version: ${{ matrix.python-version }} 25 | - name: Setup uv 26 | uses: astral-sh/setup-uv@v6 27 | - name: Install deps 28 | run: | 29 | uv sync --only-group tests 30 | uv pip install coveralls 31 | - name: Run tests 32 | env: 33 | GITHUB_TOKEN: ${{ secrets.github_token }} 34 | run: | 35 | uv run coverage run -m pytest 36 | uv run coveralls --service=github 37 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .project 2 | .pydevproject 3 | .idea 4 | .tox 5 | .venv 6 | __pycache__ 7 | *.pyc 8 | *.pyo 9 | *.egg-info 10 | 11 | -------------------------------------------------------------------------------- /AUTHORS.md: -------------------------------------------------------------------------------- 1 | # pytest-datafixtures authors 2 | 3 | Created by Igor `idle sign` Starikov. 4 | 5 | ## Contributors 6 | 7 | Here could be your name. 8 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # pytest-datafixtures changelog 2 | 3 | ### v1.1.0 [2025-05-16] 4 | * ++ Add 'datafix_dump' fixture. 5 | 6 | ### v1.0.0 [2020-12-05] 7 | * ++ 'datafix_read' now can represent result as StringIO. 8 | * ++ Added new 'datafix_readbin' fixture. 9 | 10 | 11 | ### v0.1.0 [2020-05-11] 12 | * ++ Basic functionality. -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | # pytest-datafixtures contributing 2 | 3 | ## Submit issues 4 | 5 | If you spotted something weird in application behavior or want to propose a feature you are welcome. 6 | 7 | 8 | ## Write code 9 | 10 | If you are eager to participate in application development and to work on an existing issue (whether it should 11 | be a bugfix or a feature implementation), fork, write code, and make a pull request right from the forked project page. 12 | 13 | 14 | ## Spread the word 15 | 16 | If you have some tips and tricks or any other words that you think might be of interest for the others — publish it 17 | wherever you find convenient. 18 | 19 | 20 | See also: https://github.com/idlesign/pytest-datafixtures 21 | -------------------------------------------------------------------------------- /INSTALL.md: -------------------------------------------------------------------------------- 1 | pytest-datafixtures installation 2 | ================================ 3 | 4 | 5 | Python ``pip`` package is required to install ``pytest-datafixtures``. 6 | 7 | 8 | From sources 9 | ------------ 10 | 11 | Use the following command line to install ``pytest-datafixtures`` from sources directory (containing setup.py): 12 | 13 | pip install . 14 | 15 | or 16 | 17 | python setup.py install 18 | 19 | 20 | From PyPI 21 | --------- 22 | 23 | Alternatively you can install ``pytest-datafixtures`` from PyPI: 24 | 25 | pip install pytest-datafixtures 26 | 27 | 28 | Use `-U` flag for upgrade: 29 | 30 | pip install -U pytest-datafixtures 31 | 32 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2020-2025, Igor `idle sign` Starikov 2 | All rights reserved. 3 | 4 | Redistribution and use in source and binary forms, with or without 5 | modification, are permitted provided that the following conditions are met: 6 | 7 | * Redistributions of source code must retain the above copyright notice, this 8 | list of conditions and the following disclaimer. 9 | 10 | * Redistributions in binary form must reproduce the above copyright notice, 11 | this list of conditions and the following disclaimer in the documentation 12 | and/or other materials provided with the distribution. 13 | 14 | * Neither the name of the pytest-datafixtures nor the names of its 15 | contributors may be used to endorse or promote products derived from 16 | this software without specific prior written permission. 17 | 18 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 19 | AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 20 | IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 21 | DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE 22 | FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 23 | DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 24 | SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 25 | CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 26 | OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 27 | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 28 | 29 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # pytest-datafixtures 2 | 3 | https://github.com/idlesign/pytest-datafixtures 4 | 5 | [![PyPI - Version](https://img.shields.io/pypi/v/pytest-datafixtures)](https://pypi.python.org/pypi/pytest-datafixtures) 6 | [![License](https://img.shields.io/pypi/l/pytest-datafixtures)](https://pypi.python.org/pypi/pytest-datafixtures) 7 | [![Coverage](https://img.shields.io/coverallsCoverage/github/idlesign/pytest-datafixtures)](https://coveralls.io/r/idlesign/pytest-datafixtures) 8 | 9 | ## Description 10 | 11 | *Data fixtures for pytest made simple* 12 | 13 | Offers fixtures for your tests to simplify data fixtures access. 14 | Makes use of Python's native `Path` objects. 15 | 16 | Data fixtures (files) expected to be stored in `datafixtures` directory next to your test modules: 17 | 18 | ``` 19 | tests 20 | |-- datafixtures 21 | | |-- mydatafixture.xml 22 | | |-- mydata.bin 23 | |-- test_basic.py 24 | | 25 | |-- subdirectory 26 | |---- datafixtures 27 | | |-- payload.json 28 | | |-- cert.cer 29 | |---- test_other.py 30 | ``` 31 | 32 | 33 | ## Requirements 34 | 35 | * Python 3.10+ 36 | 37 | 38 | ## Fixtures 39 | 40 | * `datafix_dir` - Path object for data fixtures directory from the current test module's directory. 41 | * `datafix` - Path object for a file in data fixtures directory with the same name as the current test function. 42 | * `datafix_read` - Returns text contents of a data fixture by name. 43 | * `datafix_readbin` - Returns binary contents of a data fixture by name. 44 | * `datafix_dump` - Allows data dumping for further usage. 45 | 46 | 47 | #### datafix_dir 48 | 49 | Access data fixtures directory: 50 | 51 | ```python 52 | def test_me(datafix_dir): 53 | 54 | # datafix_dir returns a Path object. 55 | assert datafix_dir.exists() 56 | 57 | # Gather data fixtures filenames. 58 | files = list(f'{file.name}' for file in datafix_dir.iterdir()) 59 | 60 | # Read some fixture as text. 61 | # The same as using `datafix_read` fixture (see below). 62 | filecontent = (datafix_dir / 'expected.html').read_text() 63 | 64 | # Or read binary. 65 | filecontent = (datafix_dir / 'dumped.bin').read_bytes() 66 | ``` 67 | 68 | #### datafix 69 | 70 | Access a data fixture with test name: 71 | 72 | ```python 73 | def test_me(datafix): 74 | # Read datafixtures/test_me.txt file 75 | filecontents = datafix.with_suffix('.txt').read_text() 76 | ``` 77 | 78 | #### datafix_read 79 | 80 | Access text contents of a data fixture by name: 81 | 82 | ```python 83 | def test_datafix_read(datafix_read): 84 | # Read datafixtures/expected.html file 85 | filecontents = datafix_read('expected.html') 86 | 87 | # Read encoded and represent as an StringIO object. 88 | encoded_io = datafix_read('test_datafix.txt', encoding='cp1251', io=True) 89 | ``` 90 | 91 | #### datafix_readbin 92 | 93 | Access binary contents of a data fixture by name: 94 | 95 | ```python 96 | def test_datafix_read(datafix_readbin): 97 | # Read datafixtures/dumped.bin file 98 | binary = datafix_readbin('dumped.bin') 99 | 100 | # Read binary and represent as an BytesIO object. 101 | bin_io = datafix_readbin('dumped.bin', io=True) 102 | ``` 103 | 104 | #### datafix_dump 105 | 106 | Dump data for later usage as a datafixture: 107 | 108 | ```python 109 | def test_datafix_dump(datafix_dump): 110 | # Dump text 111 | dumped_filepath = datafix_dump('sometext', encoding='cp1251') 112 | 113 | # Dump binary 114 | dumped_filepath = datafix_dump(b'\x12') 115 | ``` 116 | -------------------------------------------------------------------------------- /datafixtures/__init__.py: -------------------------------------------------------------------------------- 1 | 2 | 3 | VERSION = '1.1.0' 4 | """Application version number.""" -------------------------------------------------------------------------------- /datafixtures/entry.py: -------------------------------------------------------------------------------- 1 | from io import StringIO, BytesIO 2 | from pathlib import Path 3 | 4 | import pytest 5 | 6 | if False: # pragma nocover 7 | from _pytest.fixtures import SubRequest # noqa 8 | 9 | 10 | @pytest.fixture 11 | def datafix_dir(request: 'SubRequest') -> Path: 12 | """Returns data fixtures directory (as Path object) for test.""" 13 | return Path(request.module.__file__).absolute().parent / 'datafixtures' 14 | 15 | 16 | @pytest.fixture 17 | def datafix(datafix_dir: Path, request: 'SubRequest') -> Path: 18 | """Returns data fixture Path object for current test.""" 19 | return datafix_dir / request.node.name 20 | 21 | 22 | @pytest.fixture 23 | def datafix_dump(datafix_dir: Path, request: 'SubRequest'): 24 | """Allows dumping data to a datafixtures directory""" 25 | 26 | testname = request.node.name 27 | 28 | def datafix_dump_( 29 | data: str | bytes, 30 | fname: str = '', 31 | *, 32 | encoding: str = None, 33 | ) -> Path: 34 | 35 | fname = fname or testname 36 | target = (datafix_dir / fname) 37 | 38 | if isinstance(data, str): 39 | target.write_text(data, encoding=encoding) 40 | 41 | else: 42 | target.write_bytes(data) 43 | 44 | return target 45 | 46 | return datafix_dump_ 47 | 48 | 49 | @pytest.fixture 50 | def datafix_read(datafix_dir: Path, request: 'SubRequest'): 51 | """Returns text from the data fixture by its name.""" 52 | 53 | testname = request.node.name 54 | 55 | def datafix_read_( 56 | fname: str = None, 57 | *, 58 | encoding: str | None = None, 59 | io: bool = False 60 | 61 | ) -> str | StringIO: 62 | 63 | fname = fname or testname 64 | data = (datafix_dir / fname).read_text(encoding=encoding) 65 | if io: 66 | return StringIO(data) 67 | return data 68 | 69 | return datafix_read_ 70 | 71 | 72 | @pytest.fixture 73 | def datafix_readbin(datafix_dir: Path, request: 'SubRequest'): 74 | """Returns binary from the data fixture by its name.""" 75 | 76 | testname = request.node.name 77 | 78 | def datafix_readbin( 79 | fname: str = None, 80 | *, 81 | io: bool = False 82 | 83 | ) -> bytes | BytesIO: 84 | 85 | fname = fname or testname 86 | data = (datafix_dir / fname).read_bytes() 87 | if io: 88 | return BytesIO(data) 89 | return data 90 | 91 | return datafix_readbin 92 | -------------------------------------------------------------------------------- /pyproject.toml: -------------------------------------------------------------------------------- 1 | [project] 2 | name = "pytest-datafixtures" 3 | dynamic = ["version"] 4 | description = "Data fixtures for pytest made simple." 5 | authors = [ 6 | { name = "Igor Starikov", email = "idlesign@yandex.ru" } 7 | ] 8 | readme = "README.md" 9 | license = "BSD-3-Clause" 10 | license-files = ["LICENSE"] 11 | requires-python = ">=3.10" 12 | keywords = ["pytest", "pytest-plugin", "fixtures"] 13 | classifiers = [ 14 | "Development Status :: 5 - Production/Stable", 15 | "Operating System :: OS Independent", 16 | "Programming Language :: Python :: 3", 17 | "Programming Language :: Python :: 3.10", 18 | "Framework :: Pytest", 19 | "Intended Audience :: Developers", 20 | "Topic :: Software Development :: Testing", 21 | ] 22 | 23 | [project.urls] 24 | Homepage = "https://github.com/idlesign/pytest-datafixtures" 25 | 26 | [project.entry-points.pytest11] 27 | datafixtures = "datafixtures.entry" 28 | 29 | [dependency-groups] 30 | dev = [ 31 | {include-group = "docs"}, 32 | {include-group = "linters"}, 33 | {include-group = "tests"}, 34 | ] 35 | docs = [ 36 | ] 37 | linters = [ 38 | "ruff", 39 | ] 40 | tests = [ 41 | "pytest", 42 | "pytest-cov", 43 | "coverage", 44 | ] 45 | 46 | [build-system] 47 | requires = ["hatchling"] 48 | build-backend = "hatchling.build" 49 | 50 | [tool.hatch.version] 51 | path = "datafixtures/__init__.py" 52 | 53 | [tool.hatch.build.targets.wheel] 54 | packages = ["datafixtures"] 55 | 56 | [tool.hatch.build.targets.sdist] 57 | packages = ["datafixtures"] 58 | 59 | [tool.pytest.ini_options] 60 | testpaths = [ 61 | "tests", 62 | ] 63 | 64 | [tool.coverage.run] 65 | source = [ 66 | "datafixtures/", 67 | ] 68 | 69 | [tool.coverage.report] 70 | fail_under = 90.00 71 | exclude_also = [ 72 | "raise NotImplementedError", 73 | "if TYPE_CHECKING:", 74 | ] 75 | -------------------------------------------------------------------------------- /tests/datafixtures/fileone: -------------------------------------------------------------------------------- 1 | Раздватричетырепять 2 | -------------------------------------------------------------------------------- /tests/datafixtures/test_datafix.txt: -------------------------------------------------------------------------------- 1 | testit 2 | -------------------------------------------------------------------------------- /tests/sub/datafixtures/test_sub: -------------------------------------------------------------------------------- 1 | 12 2 | -------------------------------------------------------------------------------- /tests/sub/test_submodule.py: -------------------------------------------------------------------------------- 1 | 2 | def test_sub(datafix): 3 | filecontents = datafix.read_text() 4 | assert filecontents == '12\n' 5 | -------------------------------------------------------------------------------- /tests/test_module.py: -------------------------------------------------------------------------------- 1 | 2 | 3 | def test_datafix_dir(datafix_dir): 4 | 5 | assert datafix_dir.exists() 6 | assert f'{datafix_dir}'.endswith('/tests/datafixtures') 7 | 8 | files = list(f'{file.name}' for file in datafix_dir.iterdir()) 9 | assert 'fileone' in files 10 | 11 | file = datafix_dir / 'fileone' 12 | 13 | filecontent = file.read_text() 14 | assert filecontent == 'Раздватричетырепять\n' 15 | 16 | filecontent = file.read_bytes() 17 | assert filecontent[0] == 208 18 | 19 | 20 | def test_datafix(datafix): 21 | filecontents = datafix.with_suffix('.txt').read_text() 22 | assert filecontents == 'testit\n' 23 | 24 | 25 | def test_datafix_read(datafix_read): 26 | filecontents = datafix_read('test_datafix.txt') 27 | assert filecontents == 'testit\n' 28 | 29 | strio = datafix_read('test_datafix.txt', encoding='utf-8', io=True) 30 | assert strio.read() == 'testit\n' 31 | 32 | 33 | def test_datafix_readbin(datafix_readbin): 34 | filecontents = datafix_readbin('fileone') 35 | assert filecontents[0] == 208 36 | 37 | binio = datafix_readbin('fileone', io=True) 38 | assert binio.read(1) == b'\xd0' 39 | 40 | 41 | def test_datafix_dump(datafix_dump, datafix_read, datafix_readbin): 42 | 43 | # default naming 44 | assert f"{datafix_dump('sometext')}".endswith('test_datafix_dump') 45 | assert datafix_read() == 'sometext' 46 | 47 | # custom naming 48 | assert f"{datafix_dump('moretext', 'myfile.txt')}".endswith('myfile.txt') 49 | assert datafix_read('myfile.txt') == 'moretext' 50 | 51 | # binary 52 | assert datafix_dump(b'somebin', 'mybin.bin') 53 | assert datafix_readbin('mybin.bin') == b'somebin' 54 | --------------------------------------------------------------------------------