Repository: pytest-dev/pytest
File: testing/python/fixtures.py (Python)

Code before:
            assert last_executed == "fixture_1_setup"
            last_executed = "fixture_2_setup"
            yield
            assert last_executed == "run_test"
            last_executed = "fixture_2_teardown"


        def test_fixture_teardown_order(fixture_1: None, fixture_2: None) -> None:
            global last_executed
            assert last_executed == "fixture_2_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_subfixture_not_queue_multiple_finalizers(pytester: Pytester) -> None:
    """Make sure subfixtures don't needlessly requeue their finalizer multiple times in
    parent fixture.

    Regression test for #12135
    """
    pytester.makepyfile(
        """
        import pytest

        def _assert_len(request):
            assert len(request._get_active_fixturedef('fixture_1')._finalizers) == 1


        @pytest.fixture(scope="module")
        def fixture_1():
            ...

        @pytest.fixture(scope="module")
        def fixture_2(request, fixture_1):
            yield

        def test_1(request, fixture_2):
            _assert_len(request)

        def test_2(request, fixture_2):

Review comment (suggestion):
I know it's tempting but `pytester` tests should only test observable behavior, never using internal implementation details such as `_get_active_fixturedef` and `_finalizers`. So let's omit this test.

Code after:
            assert last_executed == "fixture_1_setup"
            last_executed = "fixture_2_setup"
            yield
            assert last_executed == "run_test"
            last_executed = "fixture_2_teardown"


        def test_fixture_teardown_order(fixture_1: None, fixture_2: None) -> None:
            global last_executed
            assert last_executed == "fixture_2_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_subfixture_teardown_order(pytester: Pytester) -> None:
    """
    Make sure fixtures don't re-register their finalization in parent fixtures multiple
    times, causing ordering failure in their teardowns.

    Regression test for #12135
    """
    pytester.makepyfile(
        """
        import pytest

        execution_order = []

        @pytest.fixture(scope="class")
        def fixture_1():
            ...

        @pytest.fixture(scope="class")
        def fixture_2(fixture_1):
            execution_order.append("setup 2")
            yield
            execution_order.append("teardown 2")

        @pytest.fixture(scope="class")
        def fixture_3(fixture_1):
            execution_order.append("setup 3")