parametrize_with_fixtures.rst
Overview
This document proposes enhancements to how **pytest** fixtures can be combined and parametrized in functional tests, particularly when using fixtures as inputs to parametrized tests or other fixtures.
The primary goal is to enable testing multiple input scenarios cleanly and effectively, especially when those inputs are derived from existing fixtures. The proposal addresses current limitations when trying to parametrize tests that depend on other parametrized fixtures, which is a common challenge in pytest testing workflows.
Context & Problem Statement
Testers often want to run a suite of functional tests against different data scenarios. For example, when generating a project from a cookiecutter template, they might want to test:
The default context values.
Variations that emulate user input, such as specifying
author,project_slug, or both.
A typical implementation might use multiple fixtures with hardcoded `params` lists and then a "combined" fixture that attempts to select between those. However, this approach is problematic:
Using request.getfuncargvalue() to dynamically access fixture values does not work well with parametrized fixtures.
Pytest 3.0+ raises errors if a fixture is referenced without defining parameters for the current test.
Extending existing tests by adding parameters to already used fixtures becomes inconvenient or impossible.
Current Example (Problematic)
import pytest
@pytest.fixture
def default_context():
return {"extra_context": {}}
@pytest.fixture(params=[
{"author": "alice"},
{"project_slug": "helloworld"},
{"author": "bob", "project_slug": "foobar"},
])
def extra_context(request):
return {"extra_context": request.param}
@pytest.fixture(params=["default", "extra"])
def context(request):
if request.param == "default":
return request.getfuncargvalue("default_context")
else:
return request.getfuncargvalue("extra_context")
def test_generate_project(cookies, context):
result = cookies.bake(extra_context=context)
assert result.exit_code == 0
assert result.exception is None
assert result.project.isdir()
This triggers errors because `extra_context` is parametrized but used dynamically via `getfuncargvalue`, which is incompatible with pytest parameter resolution.
Proposed Solution
Introduce a new pytest API function:
pytest.define_combined_fixture(
name="context",
fixtures=["default_context", "extra_context"]
)
This function creates a new fixture named `"context"` that:
Inherits the scope from the input fixtures.
Yields all possible parameter combinations from the listed fixtures.
Produces values in order, including the empty default context and all variations from
extra_context.
For the above example, the combined fixture `context` will yield:
{}{'author': 'alice'}{'project_slug': 'helloworld'}{'author': 'bob', 'project_slug': 'foobar'}
This approach resolves the dynamic fixture parameterization problem by declaratively combining fixtures.
Alternative Approach: fixture_request Helper
Another proposed helper function is `pytest.fixture_request()`. It can be used to wrap fixtures as parameters in a list, allowing tests or fixtures to parametrize over existing fixtures transparently.
Example usage:
@pytest.fixture(params=[
pytest.fixture_request("default_context"),
pytest.fixture_request("extra_context"),
])
def context(request):
return request.param
Here, `context` yields all values from `default_context` and then all values from `extra_context` in sequence.
It also works with `pytest.mark.parametrize`:
@pytest.mark.parametrize(
"context, expected_response_code",
[
(pytest.fixture_request("default_context"), 0),
(pytest.fixture_request("extra_context"), 0),
],
)
def test_generate_project(cookies, context, exit_code):
result = cookies.bake(extra_context=context)
assert result.exit_code == exit_code
This allows easy extension of tests by adding fixture-based parameters to parameterized tests.
Implementation Details
The core issue tackled is pytest's inability to naturally combine parametrized fixtures dynamically.
define_combined_fixture()would internally generate a new parametrized fixture which iterates over the Cartesian product (or a defined combination) of the supplied fixtures’ parameter sets.fixture_request()acts as a marker or proxy that pytest can interpret to resolve and yield fixture parameters during test collection.
Interactions with Other System Components
This proposal extends pytest’s core fixture and parameterization system.
It is especially relevant for projects using cookiecutter templates for project generation and testing.
The proposal references the pytest-lazy-fixture plugin, which implements a similar helper for lazy evaluation of fixtures as parameters.
Can be integrated into CI test suites to improve test coverage across multiple input scenarios without boilerplate code repetition.
Usage Examples
Define combined fixture:
pytest.define_combined_fixture(
name="context",
fixtures=["default_context", "extra_context"]
)
Use
fixture_requestto parametrize a fixture:
@pytest.fixture(params=[
pytest.fixture_request("default_context"),
pytest.fixture_request("extra_context"),
])
def context(request):
return request.param
Use
fixture_requestinpytest.mark.parametrize:
@pytest.mark.parametrize(
"context, expected_response_code",
[
(pytest.fixture_request("default_context"), 0),
(pytest.fixture_request("extra_context"), 0),
],
)
def test_generate_project(cookies, context, exit_code):
result = cookies.bake(extra_context=context)
assert result.exit_code == exit_code
Visual Diagram
flowchart TD
A[default_context fixture]
B[extra_context fixture]
C[define_combined_fixture: context]
D[test_generate_project]
A --> C
B --> C
C --> D
subgraph "Alternative approach"
E[fixture_request helper]
F[context fixture with params using fixture_request]
G[pytest.mark.parametrize using fixture_request]
H[test_generate_project with parametrize]
E --> F
E --> G
F --> H
G --> H
end
Summary
This proposal enhances pytest fixture parametrization by enabling:
The combination of multiple fixtures into a single parametrized fixture.
Parametrizing tests directly with fixtures via helper functions.
Cleaner, more scalable testing of multiple context scenarios, e.g., for cookiecutter project generation.
It overcomes key limitations in pytest's fixture resolution and parameter passing mechanisms, improving test extensibility and maintainability.
References
Pytest Fixture documentation: https://docs.pytest.org/en/stable/fixture.html
Cookiecutter testing via pytest fixtures
This concludes the comprehensive documentation for **parametrize_with_fixtures.rst**.