From 007d24a6faaa4e3eb8f4a7bc981093c2d7ce631a Mon Sep 17 00:00:00 2001 From: jakkdl Date: Sun, 3 Mar 2024 13:22:23 +0100 Subject: [PATCH] improve comment getfixturevalue, move test_scoped_fixture_teardown_order to testing/python/fixtures.py --- src/_pytest/fixtures.py | 7 ++- testing/python/fixtures.py | 54 +++++++++++++++++++ .../test_scope_fixture_teardown_order.py | 36 ------------- 3 files changed, 59 insertions(+), 38 deletions(-) delete mode 100644 testing/python/test_scope_fixture_teardown_order.py diff --git a/src/_pytest/fixtures.py b/src/_pytest/fixtures.py index 532330fa0..fa727513c 100644 --- a/src/_pytest/fixtures.py +++ b/src/_pytest/fixtures.py @@ -531,8 +531,11 @@ class FixtureRequest(abc.ABC): :raises pytest.FixtureLookupError: If the given fixture could not be found. """ - # Note: This is called during setup for evaluating fixtures defined via - # function arguments as well. + # Note that in addition to the use case described in the docstring, + # getfixturevalue() is also called by pytest itself during item setup to + # evaluate the fixtures that are requested statically + # (using function parameters, autouse, etc). + fixturedef = self._get_active_fixturedef(argname) assert fixturedef.cached_result is not None, ( f'The fixture value for "{argname}" is not available. ' diff --git a/testing/python/fixtures.py b/testing/python/fixtures.py index 1b7a0e414..0758b590c 100644 --- a/testing/python/fixtures.py +++ b/testing/python/fixtures.py @@ -4561,6 +4561,60 @@ def test_deduplicate_names() -> None: assert items == ("a", "b", "c", "d", "g", "f", "e") +def test_scoped_fixture_teardown_order(pytester: Pytester) -> None: + """ + Make sure teardowns happen in reverse order of setup with scoped fixtures, when + a later test only depends on a subset of scoped fixtures. + Regression test for https://github.com/pytest-dev/pytest/issues/1489 + """ + pytester.makepyfile( + """ + from typing import Generator + + import pytest + + + last_executed = "" + + + @pytest.fixture(scope="module") + def fixture_1() -> Generator[None, None, None]: + global last_executed + assert last_executed == "" + last_executed = "autouse_setup" + yield + assert last_executed == "noautouse_teardown" + last_executed = "autouse_teardown" + + + @pytest.fixture(scope="module") + def fixture_2() -> Generator[None, None, None]: + global last_executed + assert last_executed == "autouse_setup" + last_executed = "noautouse_setup" + yield + assert last_executed == "run_test" + last_executed = "noautouse_teardown" + + + def test_autouse_fixture_teardown_order(fixture_1: None, fixture_2: None) -> None: + global last_executed + assert last_executed == "noautouse_setup" + last_executed = "run_test" + + + def test_2(fixture_1: None) -> None: + # this would previously queue an additional teardown of fixture_1, + # despite fixture_1's value being cached, which caused fixture_1 to be + # torn down before fixture_2 - violating the rule that teardowns should + # happen in reverse order of setup. + pass + """ + ) + result = pytester.runpytest() + assert result.ret == 0 + + def test_scope_fixture_caching_1(pytester: Pytester) -> None: """ Make sure setup and finalization is only run once when using fixture diff --git a/testing/python/test_scope_fixture_teardown_order.py b/testing/python/test_scope_fixture_teardown_order.py deleted file mode 100644 index 8ce97cd32..000000000 --- a/testing/python/test_scope_fixture_teardown_order.py +++ /dev/null @@ -1,36 +0,0 @@ -from typing import Generator - -import pytest - - -last_executed = "" - - -@pytest.fixture(scope="module") -def fixture_1() -> Generator[None, None, None]: - global last_executed - assert last_executed == "" - last_executed = "autouse_setup" - yield - assert last_executed == "noautouse_teardown" - last_executed = "autouse_teardown" - - -@pytest.fixture(scope="module") -def fixture_2() -> Generator[None, None, None]: - global last_executed - assert last_executed == "autouse_setup" - last_executed = "noautouse_setup" - yield - assert last_executed == "run_test" - last_executed = "noautouse_teardown" - - -def test_autouse_fixture_teardown_order(fixture_1: None, fixture_2: None) -> None: - global last_executed - assert last_executed == "noautouse_setup" - last_executed = "run_test" - - -def test_2(fixture_1: None) -> None: - pass