pytest-dev / pytest

The pytest framework makes it easy to write small tests, yet scales to support complex functional testing
https://pytest.org
MIT License
11.96k stars 2.66k forks source link

Missing entry in metafunc.fixturenames for sub-fixture with same name #3716

Open whimboo opened 6 years ago

whimboo commented 6 years ago

Having a sub-fixture overwriting a higher-level fixture (with the same name) an argument setup as marker is lost for the higher-level fixture. This is tested with latest pytest 3.6.3.

Here an example:

# conftest.py
import pytest

@pytest.fixture(name="fish")
def fixture_fish(taste):
    pass

def pytest_generate_tests(metafunc):
    if "taste" in metafunc.fixturenames:
        print("** found taste")
        marker = metafunc.definition.get_closest_marker(name="taste")
        if marker:
            metafunc.parametrize("taste", marker.args, ids=None)
    else:
        print("** didn't find taste")
# test.py
import pytest

@pytest.fixture(name="fish")
def fixture_fish(fish):  # , taste):
    yield fish

@pytest.mark.taste("dry")
def test_fish(fish):
    pass

Workarounds for now are:

1) Use a different name for the sub-fixture 2) Also adding the argument for the sub-fixture

I would expect that I can simply overwrite fixtures and markers are still available as arguments for fixtures.

pytestbot commented 6 years ago

GitMate.io thinks possibly related issues are https://github.com/pytest-dev/pytest/issues/3655 (several names for fixture), https://github.com/pytest-dev/pytest/issues/794 (fixture named "request" fails), https://github.com/pytest-dev/pytest/issues/519 (fixture scope is ignored when using metafunc.parametrize()), https://github.com/pytest-dev/pytest/issues/354 (tmpdir fixture: 'File name too long'), and https://github.com/pytest-dev/pytest/issues/1368 (apply markers based on fixtures/dependent fixtures).

davehunt commented 6 years ago

I can replicate this with the following test:

def test_extend_fixture_conftest_module_alter_fixtures(self, testdir):
    testdir.makeconftest("""
        import pytest

        @pytest.fixture
        def ham(): return 'ham'

        @pytest.fixture
        def spam(ham): return ham + 'spam'
    """)
    testfile = testdir.makepyfile("""
        import pytest

        @pytest.fixture
        def spam(spam):
            return spam * 2

        def test_spam(request, spam):
            assert spam == 'hamspamhamspam'
            assert 'ham' in request.fixturenames
    """)
    result = testdir.runpytest()
    result.stdout.fnmatch_lines(["*1 passed*"])
    result = testdir.runpytest(testfile)
    result.stdout.fnmatch_lines(["*1 passed*"])

It fails with:

    def test_spam(request, spam):
        assert spam == 'hamspamhamspam'
>       assert 'ham' in request.fixturenames
E       AssertionError: assert 'ham' in ['request', 'spam']
E        +  where ['request', 'spam'] = <FixtureRequest for <Function 'test_spam'>>.fixturenames