ValueError: 'indirect' cannot be used with 'ids' – parametrize limitation
0 reputation · 15 Mar 2024, 19:01 UTC
0 reputation · 15 Mar 2024, 19:01 UTC
When writing tests that need both readable identifiers and indirect fixture parametrization—for example, to generate timezone‑aware datetime objects for cross‑region date validation—developers encounter a ValueError if they try to combine ids with indirect=True in @pytest.mark.parametrize. The current pytest implementation treats this combination as unsupported, forcing a choice between meaningful test names and indirect fixture usage.
This restriction raises uncertainty about whether the limitation will be lifted in a future release, whether a workaround is officially recommended, or if users should adopt alternative strategies such as custom id functions or separate parametrization steps.
ids together with indirect=True in an upcoming version?Pytest does not allow the ids argument together with indirect=True in a single @pytest.mark.parametrize call.
The ids list is processed before pytest resolves the fixture indicated by indirect. When indirect=True is set, the values passed to the test are the results of calling the fixture, not the raw parameters. Because the identifier must be known before the fixture runs, pytest treats the combination as unsupported and raises ValueError: 'indirect' cannot be used with 'ids'.
Embed the identifier directly in each parameter using pytest.param. This works with indirect=True because the identifier is attached to the raw value before the fixture is invoked.
import pytest
from datetime import datetime, timezone, timedelta
def make_tz_aware(offset_hours):
tz = timezone(timedelta(hours=offset_hours))
return lambda: datetime.now(tz)
@pytest.fixture
def tz_now(request):
# request.param is the factory returned by make_tz_aware
return request.param()
@pytest.mark.parametrize(
"tz_now",
[
pytest.param(make_tz_aware(-5), id="EST"),
pytest.param(make_tz_aware(0), id="UTC"),
pytest.param(make_tz_aware(9), id="JST"),
],
indirect=True,
)
def test_cross_region_date(tz_now):
assert tz_now.tzinfo is not None
# … assertions that depend on the zone …
Each test case now appears in the output as test_cross_region_date[EST], [UTC], [JST].
idsIf the identifier depends only on the raw parameter (not on the fixture result), you can move the ids to the fixture definition:
@pytest.fixture(params=[-5, 0, 9], ids=lambda o: { -5: "EST", 0: "UTC", 9: "JST" }[o])
def tz_now(request):
offset = request.param
tz = timezone(timedelta(hours=offset))
return datetime.now(tz)
def test_cross_region_date(tz_now):
assert tz_now.tzinfo is not None
This achieves the same readable names without needing indirect in the parametrize decorator.
To confirm that the workaround works for you, run:
pytest --collect-only test_example.py::test_cross_region_date
You should see the three test variants with the expected IDs.
As of pytest 8.0 the restriction remains; the development team has indicated that combining ids with indirect would require a redesign of the parametrization pipeline. No timeline has been announced, so relying on a future lift is not advisable.
If you are using a version older than pytest 6.0 and notice different behavior, please let us know the exact pytest version and the full traceback; that information would change the recommendation.
Use comments to ask for clarification. Post a solution as an answer.
No question comments on this page.