pytest-cases

Separate test code from test cases in pytest, with fixture unions and parametrization goodies for readable, reusable test suites.

Library
PyPI
v3.10.1
376stars
BSD 3-Clause License

Repository Health

Pre-computed score based on development activity, maintenance, community, maturity, and trend momentum.How we score it →
49/100Fair
Development Activity4
Maintenance48
Community64
Maturity60
Momentum20

Technical Analysis

AI-assessed by reading the actual repository — architecture, code quality, innovation, and documentation.How we score it →
72/100Good
Architecture74
Code Quality78
Innovation72
Learning Curve65

pytest-cases extends pytest’s @pytest.mark.parametrize model so that test cases can live independently from test functions. Instead of cramming inline parameter lists into a test file, you write case functions (or case classes) in a companion module, and decorate your test with @parametrize_with_cases to have pytest-cases collect, generate, and feed them into your test automatically, complete with readable auto-generated test IDs.

Beyond case separation, the library rewrites core pieces of pytest’s fixture machinery to support “fixture unions” — the ability to use a fixture reference as a parameter value and let a single test run across several alternative fixtures without duplicating test code. It also ships lazy_value for deferred parameter computation, @fixture as a drop-in @pytest.fixture replacement with extra powers, and tight interoperability with pytest-harvest for benchmark-style result collection. The result is a small, focused toolkit for teams whose test suites have outgrown plain parametrize but don’t want to hand-roll fixture composition themselves.

What You Get

  • @parametrize_with_cases decorator that discovers case functions/classes in a companion module and feeds them into a test, with automatic readable test IDs
  • @fixture and @parametrize drop-in replacements for @pytest.fixture/@pytest.mark.parametrize with extended capabilities (union support, lazy values, ids)
  • fixture_union and fixture_ref to let a test consume one of several alternative fixtures without duplicating the test body
  • lazy_value for parameter values that are computed only when a specific test case actually runs, instead of at collection time
  • Case tagging and filtering (@case(tags=...), matches_tag_query) to select subsets of cases to run
  • get_current_cases/current_cases fixture to introspect which case produced the current test’s parameters, useful for benchmark-style reporting with pytest-harvest

Common Use Cases

  • Moving large, hand-maintained @pytest.mark.parametrize argument lists out of test files and into dedicated, documented case modules
  • Testing a function against many structurally different inputs (dataframes, files, generated data) where each case needs its own setup code
  • Running the same test body against several interchangeable fixtures (e.g. different backends or drivers) via fixture unions instead of copy-pasted tests
  • Building small data-science or ML benchmarks that compare several models/datasets, combined with pytest-harvest to collect timing and results
  • Sharing a library of reusable case functions across multiple test files or even multiple projects

Under The Hood

Architecture The package is organized as a set of composable pytest internals extensions rather than a single monolithic plugin: case_parametrizer_new.py implements case discovery and the @parametrize_with_cases decorator, fixture_core1_unions.py and fixture_core2.py implement fixture unions and the extended @fixture decorator, and fixture_parametrize_plus.py implements the extended @parametrize/fixture_ref mechanism, with plugin.py wiring these into pytest’s hook system (via pytest_generate_tests-style hooks) so behavior activates automatically once the package is installed. This layered structure means the case-discovery layer and the fixture-union layer can be reasoned about mostly independently, even though both ultimately reach into pytest’s private CallSpec2/collection internals to inject their extended parametrization logic.

Tech Stack Written in pure Python with no runtime dependencies beyond pytest itself, plus decopatch and makefun for building well-signatured decorators, and packaging for version comparisons. The project targets Python 3.9 through 3.14 and pytest 6 through 9, using setuptools with setuptools_scm for git-tag-based versioning and nox to run the test matrix across supported interpreter/pytest combinations.

Code Quality The repository ships an extensive test suite (193 test files under tests/) covering case discovery, fixture unions, parametrization edge cases, and a dedicated pytest_extension suite that exercises the plugin against pytest’s own test harness. CI runs the full nox matrix across Python and pytest versions on GitHub Actions, with flake8 linting (max line length 120, custom copyright-header check) and generated coverage/JUnit reports published to GitHub Pages. Code favors defensive compatibility shims (try/except ImportError fallbacks for cross-version pytest internals) over strict typing, reflecting its role as a plugin that must track multiple pytest major versions simultaneously.

What Makes It Unique Most pytest parametrization helpers only add convenience on top of @pytest.mark.parametrize; pytest-cases instead reaches into pytest’s fixture resolution engine to support genuine “fixture unions” — parametrizing a test across a set of fixtures rather than plain values, something pytest has no native mechanism for. Combined with lazy-evaluated parameter values and case discovery across files or classes, it turns test-case management into a first-class, reusable abstraction rather than an ad hoc collection of parametrize lists.

Join founders buildingwith open source

Opinionated takes, migration guides, cost-saving tips, and insights from the open source ecosystem.

Subscribe on Substack
Join 750+ subscribers

Search