From 487659d8b13b6adfb1db23225c71a485cd0117bb Mon Sep 17 00:00:00 2001 From: Andrzej Klajnert Date: Wed, 28 Aug 2019 19:50:13 +0200 Subject: [PATCH 1/2] Fix the scope behavior with indirect fixtures. --- AUTHORS | 1 + changelog/570.bugfix.rst | 1 + src/_pytest/fixtures.py | 7 ++++-- testing/python/fixtures.py | 45 +++++++++++++++++++++++++++++++++++++- 4 files changed, 51 insertions(+), 3 deletions(-) create mode 100644 changelog/570.bugfix.rst diff --git a/AUTHORS b/AUTHORS index 1dbef3d5d..e355f01e0 100644 --- a/AUTHORS +++ b/AUTHORS @@ -23,6 +23,7 @@ Andras Tim Andrea Cimatoribus Andreas Zeidler Andrey Paramonov +Andrzej Klajnert Andrzej Ostrowski Andy Freeland Anthon van der Neut diff --git a/changelog/570.bugfix.rst b/changelog/570.bugfix.rst new file mode 100644 index 000000000..8936ff96a --- /dev/null +++ b/changelog/570.bugfix.rst @@ -0,0 +1 @@ +Fix the scope behavior with indirect fixtures. diff --git a/src/_pytest/fixtures.py b/src/_pytest/fixtures.py index d5f9ad2d3..9a904758f 100644 --- a/src/_pytest/fixtures.py +++ b/src/_pytest/fixtures.py @@ -859,7 +859,7 @@ class FixtureDef: if argname != "request": fixturedef.addfinalizer(functools.partial(self.finish, request=request)) - my_cache_key = request.param_index + my_cache_key = self.cache_key(request) cached_result = getattr(self, "cached_result", None) if cached_result is not None: result, cache_key, err = cached_result @@ -877,6 +877,9 @@ class FixtureDef: hook = self._fixturemanager.session.gethookproxy(request.node.fspath) return hook.pytest_fixture_setup(fixturedef=self, request=request) + def cache_key(self, request): + return request.param_index if not hasattr(request, "param") else request.param + def __repr__(self): return "".format( self.argname, self.scope, self.baseid @@ -913,7 +916,7 @@ def pytest_fixture_setup(fixturedef, request): kwargs[argname] = result fixturefunc = resolve_fixture_function(fixturedef, request) - my_cache_key = request.param_index + my_cache_key = fixturedef.cache_key(request) try: result = call_fixture_func(fixturefunc, request, kwargs) except TEST_OUTCOME: diff --git a/testing/python/fixtures.py b/testing/python/fixtures.py index 1f383e752..a129aa582 100644 --- a/testing/python/fixtures.py +++ b/testing/python/fixtures.py @@ -449,7 +449,8 @@ class TestFillFixtures: "*ERROR at setup of test_lookup_error*", " def test_lookup_error(unknown):*", "E fixture 'unknown' not found", - "> available fixtures:*a_fixture,*b_fixture,*c_fixture,*d_fixture*monkeypatch,*", # sorted + "> available fixtures:*a_fixture,*b_fixture,*c_fixture,*d_fixture*monkeypatch,*", + # sorted "> use 'py*test --fixtures *' for help on them.", "*1 error*", ] @@ -4009,3 +4010,45 @@ def test_fixture_named_request(testdir): " *test_fixture_named_request.py:5", ] ) + + +def test_indirect_fixture(testdir): + testdir.makepyfile( + """ + from collections import Counter + + import pytest + + + @pytest.fixture(scope="session") + def fixture_1(request, count=Counter()): + count[request.param] += 1 + yield count[request.param] + + + @pytest.fixture(scope="session") + def fixture_2(request): + yield request.param + + + scenarios = [ + ("a", "a1"), + ("a", "a2"), + ("b", "b1"), + ("b", "b2"), + ("c", "c1"), + ("c", "c2"), + ] + + + @pytest.mark.parametrize( + "fixture_1,fixture_2", scenarios, indirect=["fixture_1", "fixture_2"] + ) + def test_it(fixture_1, fixture_2): + assert fixture_1 == 1 + assert fixture_2[1] in ("1", "2") + + """ + ) + result = testdir.runpytest() + result.assert_outcomes(passed=6) From 35b3b1097ffc576b11cd8ad205103666a90e2344 Mon Sep 17 00:00:00 2001 From: Bruno Oliveira Date: Fri, 30 Aug 2019 10:54:07 -0300 Subject: [PATCH 2/2] Improve CHANGELOG and make test easier to understand for #570 --- changelog/570.bugfix.rst | 3 ++- testing/python/fixtures.py | 46 +++++++++++++++++++++++--------------- 2 files changed, 30 insertions(+), 19 deletions(-) diff --git a/changelog/570.bugfix.rst b/changelog/570.bugfix.rst index 8936ff96a..980491e28 100644 --- a/changelog/570.bugfix.rst +++ b/changelog/570.bugfix.rst @@ -1 +1,2 @@ -Fix the scope behavior with indirect fixtures. +Fixed long standing issue where fixture scope was not respected when indirect fixtures were used during +parametrization. diff --git a/testing/python/fixtures.py b/testing/python/fixtures.py index a129aa582..d998e9726 100644 --- a/testing/python/fixtures.py +++ b/testing/python/fixtures.py @@ -4012,43 +4012,53 @@ def test_fixture_named_request(testdir): ) -def test_indirect_fixture(testdir): +def test_indirect_fixture_does_not_break_scope(testdir): + """Ensure that fixture scope is respected when using indirect fixtures (#570)""" testdir.makepyfile( """ - from collections import Counter - import pytest + instantiated = [] @pytest.fixture(scope="session") - def fixture_1(request, count=Counter()): - count[request.param] += 1 - yield count[request.param] + def fixture_1(request): + instantiated.append(("fixture_1", request.param)) @pytest.fixture(scope="session") def fixture_2(request): - yield request.param + instantiated.append(("fixture_2", request.param)) scenarios = [ - ("a", "a1"), - ("a", "a2"), - ("b", "b1"), - ("b", "b2"), - ("c", "c1"), - ("c", "c2"), + ("A", "a1"), + ("A", "a2"), + ("B", "b1"), + ("B", "b2"), + ("C", "c1"), + ("C", "c2"), ] - @pytest.mark.parametrize( "fixture_1,fixture_2", scenarios, indirect=["fixture_1", "fixture_2"] ) - def test_it(fixture_1, fixture_2): - assert fixture_1 == 1 - assert fixture_2[1] in ("1", "2") + def test_create_fixtures(fixture_1, fixture_2): + pass + + def test_check_fixture_instantiations(): + assert instantiated == [ + ('fixture_1', 'A'), + ('fixture_2', 'a1'), + ('fixture_2', 'a2'), + ('fixture_1', 'B'), + ('fixture_2', 'b1'), + ('fixture_2', 'b2'), + ('fixture_1', 'C'), + ('fixture_2', 'c1'), + ('fixture_2', 'c2'), + ] """ ) result = testdir.runpytest() - result.assert_outcomes(passed=6) + result.assert_outcomes(passed=7)