A pytest fixture supplies test-owned setup and cleanup so cases can exercise an operation without sharing accidental state.
Pytest fixtures: isolated files and parameterized rejection tests
Operation contract
The fixture creates a tiny test module in an owned temporary directory and runs pytest in a child interpreter. The tmp_path fixture gives the accepted-input test its own file. Parameterized rejection cases cover a negative quantity, a boolean and text. The subprocess checks the exit code rather than depending on pytest’s version-specific progress text.
Failure and ownership boundary
This lesson disables automatic third-party plugin loading in the child environment and names its test file explicitly. A passing fixture does not prove every caller or deployment path is safe. Test isolation must extend to databases, clocks and network collaborators when those enter the operation. Python unittest.mock: inject a collaborator failure before publication and Python unittest: test rejected input and preserve state after failure illustrate alternative test boundaries.
Tested environment
Dependency check: this program was executed on CPython 3.14.6 with pytest==9.1.1. Install these versions in a separate virtual environment. The download includes the recorded environment snapshot; no third-party package is part of the website runtime.
Working program
from pathlib import Path
import os
import subprocess
import sys
import tempfile
TEST_SOURCE = """import pytest
def accepted_quantity(value):
if type(value) is not int or not 0 <= value <= 8:
raise ValueError('quantity rejected')
return value
def test_owned_file(tmp_path):
manifest = tmp_path / 'quantity.txt'
manifest.write_text('3', encoding='utf-8')
assert accepted_quantity(int(manifest.read_text(encoding='utf-8'))) == 3
@pytest.mark.parametrize('quantity', [-1, True, '3'])
def test_rejected_quantity(quantity):
with pytest.raises(ValueError):
accepted_quantity(quantity)
"""
with tempfile.TemporaryDirectory() as directory:
target = Path(directory) / "test_quantity.py"
target.write_text(TEST_SOURCE, encoding="utf-8")
environment = dict(os.environ, PYTEST_DISABLE_PLUGIN_AUTOLOAD="1")
result = subprocess.run([sys.executable, "-I", "-m", "pytest", "-q", str(target)],
cwd=directory, env=environment, capture_output=True, text=True, timeout=10)
if result.returncode:
raise RuntimeError("owned pytest cases failed")
print("four owned pytest cases passed")Output
four owned pytest cases passedCosts and limits
The four cases perform bounded local work. Interpreter startup and test collection dominate this small run; no speed comparison with unittest is claimed. Real integration suites must budget their external resources separately.
Common Mistakes
- Do not let tests share mutable files without explicit ownership.
- Assert the operation’s contract rather than copying its implementation into the expected result.
Connected lessons
Python unittest: test rejected input and preserve state after failure, Python unittest.mock: inject a collaborator failure before publication, Python input exercise: accept an explicit integer grammar.
Follow the related contract
Hypothesis property tests: compare generated cases with an independent contract, Coverage.py branch coverage: execution paths are not correctness assertions.
Trace the related workflow
Python IsolatedAsyncioTestCase: own async setup and cleanup per test.
