最新消息:雨落星辰是一个专注网站SEO优化、网站SEO诊断、搜索引擎研究、网络营销推广、网站策划运营及站长类的自媒体原创博客

python - Caching of parameterized, nested fixtures in pytest - Stack Overflow

programmeradmin4浏览0评论

I am trying to understand how and when return values from pytest fixtures are cached. In my understanding, the goal of fixtures (in particular session-scoped fixtures) is that they are called only once and that return values are cached for future calls. This does not seem to be the case for nested parameterized fixtures.

The following code shows the issue:

from collections import Counter

import pytest

state = Counter()


@pytest.fixture(scope="session", autouse=True)
def setup_session():
    yield None
    print(state)


@pytest.fixture(scope="session", params=["A1", "A2", "A3"])
def first(request):
    state[request.param] += 1
    return request.param


@pytest.fixture(scope="session", params=["B1", "B2"])
def second(first, request):
    return first + request.param


@pytest.fixture(scope="session", params=["C1", "C2"])
def third(second, request):
    return second + request.param


def test_length(third):
    assert len(third) == 6

The output is Counter({'A1': 3, 'A2': 2, 'A3': 1}). So first is called three times for parameter value A1, two times for A2, and once for A3. Why?

I am expecting to get Counter({'A1': 1, 'A2': 1, 'A3': 1}) - one call for each parameter value.

In case that's relevant, I am using Python 3.12.3, pytest-8.3.4

PS: All 12 tests pass - that's fine. PPS: When removing one level of nesting, the problem disappears and first is called exactly once per parameter value.

I am trying to understand how and when return values from pytest fixtures are cached. In my understanding, the goal of fixtures (in particular session-scoped fixtures) is that they are called only once and that return values are cached for future calls. This does not seem to be the case for nested parameterized fixtures.

The following code shows the issue:

from collections import Counter

import pytest

state = Counter()


@pytest.fixture(scope="session", autouse=True)
def setup_session():
    yield None
    print(state)


@pytest.fixture(scope="session", params=["A1", "A2", "A3"])
def first(request):
    state[request.param] += 1
    return request.param


@pytest.fixture(scope="session", params=["B1", "B2"])
def second(first, request):
    return first + request.param


@pytest.fixture(scope="session", params=["C1", "C2"])
def third(second, request):
    return second + request.param


def test_length(third):
    assert len(third) == 6

The output is Counter({'A1': 3, 'A2': 2, 'A3': 1}). So first is called three times for parameter value A1, two times for A2, and once for A3. Why?

I am expecting to get Counter({'A1': 1, 'A2': 1, 'A3': 1}) - one call for each parameter value.

In case that's relevant, I am using Python 3.12.3, pytest-8.3.4

PS: All 12 tests pass - that's fine. PPS: When removing one level of nesting, the problem disappears and first is called exactly once per parameter value.

Share Improve this question asked Jan 20 at 13:28 Martin LacknerMartin Lackner 1231 silver badge5 bronze badges
Add a comment  | 

1 Answer 1

Reset to default 2

From the docs (Note box)

Pytest only caches one instance of a fixture at a time, which means that when using a parametrized fixture, pytest may invoke a fixture more than once in the given scope.

If you look closely at the console output you will see that you count the changes in the returned value from first

test_length[C1-B1-A1] -> {'A1': 1, 'A2': 0, 'A3': 0}
test_length[C1-B1-A2] -> {'A1': 1, 'A2': 1, 'A3': 0}
test_length[C1-B2-A2]
test_length[C2-B2-A2]
test_length[C2-B2-A1] -> {'A1': 2, 'A2': 1, 'A3': 0}
test_length[C2-B2-A3] -> {'A1': 2, 'A2': 1, 'A3': 1}
test_length[C2-B1-A3]
test_length[C1-B2-A3]
test_length[C1-B1-A3]
test_length[C2-B1-A2] -> {'A1': 2, 'A2': 2, 'A3': 1}
test_length[C2-B1-A1] -> {'A1': 3, 'A2': 2, 'A3': 1}
test_length[C1-B2-A1]

PPS: reducing the nesting might give you the expected results, but not necessarily

def test_length(second):

output was Counter({'A1': 2, 'A2': 1, 'A3': 1})

发布评论

评论列表(0)

  1. 暂无评论