Skip to content

Defining differently-scoped fixtures with single function messes up fixture scope #2334

@Feuermurmel

Description

@Feuermurmel

I'm not sure whether this is a bug. I have a fixture which automatically cleans up resources that are expensive to keep around (like using a lot of RAM). Some of the resources allocated via the fixture are just needed for a single test and some are better shared between test because allocation takes time. Seems simple enough, I'll just define two fixtures with different scopes which share the actual implementation:

import pytest

def _fixture_impl(request):
    print('enter')
    yield
    print('leave')

@pytest.fixture(scope='function')
def the_function_fixture():
   yield from _fixture_impl()

@pytest.fixture(scope='module')
def the_module_fixture():
    yield from _fixture_impl()

@pytest.mark.parametrize('a', range(3))
def test_foo(the_module_fixture, the_function_fixture, a):
    print(a)

This works as expected. One of the fixtures gets set up and torn down once, the other for each test:

$ pytest -xs test.py
============================= test session starts ==============================
platform darwin -- Python 3.5.3, pytest-3.0.7, py-1.4.33, pluggy-0.4.0
rootdir: /Users/michi/test-pytest, inifile: pytest.ini
collected 3 items 

test.py enter
enter
0
.leave
enter
1
.leave
enter
2
.leave
leave


=========================== 3 passed in 0.02 seconds ===========================

But, I thought, I can shorten that code! Just apply the fixture() decorator to the function twice!

import pytest

def _fixture_impl():
    print('enter')
    yield
    print('leave')

the_function_fixture = pytest.fixture(scope='function')(_fixture_impl)
the_module_fixture = pytest.fixture(scope='module')(_fixture_impl)

@pytest.mark.parametrize('a', range(3))
def test_foo(the_module_fixture, the_function_fixture, a):
    print(a)

But this has the unfortunate side effect that now both fixtures are just set up and torn down once:

$ pytest -xs test.py
============================= test session starts ==============================
platform darwin -- Python 3.5.3, pytest-3.0.7, py-1.4.33, pluggy-0.4.0
rootdir: /Users/michi/test-pytest, inifile: pytest.ini
collected 3 items 

test.py enter
enter
0
.1
.2
.leave
leave


=========================== 3 passed in 0.02 seconds ===========================

It seems that the scope is declared for the function implementing the fixture and not once for each invocation of the decorator.

I did not expect that behavior. I would have assumed that the two versions would work equivalently. I do not know whether this usage pattern of the fixture() decorator is common. In my case it saves a bit redundant code as the fixture in question uses a lot of other fixtures, and *args does not work in this case. :)

This happens at least with pytest 3.0.7 on OS X 10.11.6. I'm happy to test more combinations, if necessary.

Installed packages:

$ pip list
DEPRECATION: The default format will switch to columns in the future. You can use --format=(legacy|columns) (or define a format=(legacy|columns) in your pip.conf under the [list] section) to disable this warning.
appdirs (1.4.3)
packaging (16.8)
pip (9.0.1)
py (1.4.33)
pyparsing (2.2.0)
pytest (3.0.7)
setuptools (34.3.3)
six (1.10.0)
wheel (0.30.0a0)

Metadata

Metadata

Assignees

No one assigned

    Labels

    status: help wanteddevelopers would like help from experts on this topictype: refactoringinternal improvements to the code

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions