8

I want to share fixtures between different instantiations of the same parametrized tests, where the fixtures themselves are also parametrized:

#!/usr/bin/py.test -sv

import pytest

numbers_for_fixture = [0]

def pytest_generate_tests(metafunc):
    if "config_field" in metafunc.fixturenames:
        metafunc.parametrize("config_field", [1], scope='session')

@pytest.fixture(scope = 'session')
def fixture_1(config_field):
    numbers_for_fixture[0] += 1
    return '\tfixture_1(%s)' % numbers_for_fixture[0]

@pytest.fixture(scope = 'session')
def fixture_2():
    numbers_for_fixture[0] += 1
    return '\tfixture_2(%s)' % numbers_for_fixture[0]

def test_a(fixture_1):
    print('\ttest_a:', fixture_1)

def test_b(fixture_1):
    print('\ttest_b:', fixture_1)

@pytest.mark.parametrize('i', range(3))
def test_c(fixture_1, i):
    print('\ttest_c[%s]:' % i, fixture_1)

@pytest.mark.parametrize('i', range(3))
def test_d(fixture_2, i):
    print('\ttest_d[%s]:' % i, fixture_2)

I get this output:

platform linux -- Python 3.4.1 -- py-1.4.26 -- pytest-2.6.4 -- /usr/bin/python
collecting ... collected 8 items

test.py::test_a[1]      test_a:     fixture_1(1)
PASSED
test.py::test_b[1]      test_b:     fixture_1(1)
PASSED
test.py::test_c[1-0]    test_c[0]:  fixture_1(1)
PASSED
test.py::test_c[1-1]    test_c[1]:  fixture_1(2)
PASSED
test.py::test_c[1-2]    test_c[2]:  fixture_1(3)
PASSED
test.py::test_d[0]      test_d[0]:  fixture_2(4)
PASSED
test.py::test_d[1]      test_d[1]:  fixture_2(4)
PASSED
test.py::test_d[2]      test_d[2]:  fixture_2(4)
PASSED

test_a, test_b and test_c[0] all share fixture_1(1). All the test_ds share fixture_2(4). The problem is that the test_cs use different versions of fixture_1.

This also happens when scopes are set to "module" and "class", and it only happens when both the test and the fixture are parametrized.

From the way pytest prints the test parametesr, it seems like it doesn't distinguish between the different types of parameters used for each item, so it creates a fixture for each set of parameters rather then for each unique subset of a parameters list that the fixture uses.

Is this a bug in pytest, or did I neglect to set some configuration or something? Is there a workaround?

Ignacio Vergara Kausel
  • 5,521
  • 4
  • 31
  • 41
Idan Arye
  • 12,402
  • 5
  • 49
  • 68
  • It seems to be a reported bug - https://bitbucket.org/hpk42/pytest/issue/634/metafuncparametrize-overwrites-scope – Idan Arye Dec 07 '14 at 16:32
  • updated link https://bitbucket.org/pytest-dev/pytest/issue/634/metafuncparametrize-overwrites-scope – Efren May 25 '15 at 03:25
  • This issue is not completely solved. See the related issue https://github.com/pytest-dev/pytest/issues/1697 – Andriy Jul 04 '16 at 09:53
  • Take a look in this [question](https://stackoverflow.com/questions/37351349/returning-multiple-objects-from-py-test-fixture/37351707#37351707) might help you. – Ederson Badeca Jul 05 '18 at 16:11

2 Answers2

2

In a nut shell, there's a bug where the scoping is effectively ignored in this case. Run pytest with the --setup-show switch to get a better view of when the fixtures are set up and torn down.

Workaround

Comment out the pytest_generate_tests function and add the following:

@pytest.fixture('session', [1])
def config_field(request):
    return request.param

Now you should see the correct set up and tear down nesting.

Workaround for dynamic parameters

I needed to set my parameters from a cli agument and had to use pytest_generate_tests

Set indirect to True and paramters to the cli argument:

def pytest_generate_tests(metafunc):
    if "config_field" in metafunc.fixturenames:
        metafunc.parametrize(
            "config_field",
            metafunc.config.getoption('some_option'),
            True,
            'session'
        )

Add a place holder fixture:

@pytest.fixture('session')
def config_field(request):
    return request.param
Guy Gangemi
  • 1,533
  • 1
  • 13
  • 25
0

Well, You can return yield from your feature, this might return the same value:

@pytest.fixture(scope = 'session')
def fixture_1(config_field):
   .. yield <fixture-properties>
Ederson Badeca
  • 363
  • 1
  • 9