Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Teardown of fixture with scope=class is not called if following test passes only after rerun or fails after rerun #241

Closed
ExplorerOL opened this issue Nov 9, 2023 · 4 comments · Fixed by #260
Labels
help wanted a pull request to fix this issue is welcome

Comments

@ExplorerOL
Copy link

ExplorerOL commented Nov 9, 2023

Good day! I found out a problem, that teardown of fixture with scope=class not called if following test passed only after rerun or fails after rerun. I tend to think that problem is in plugin pytest-rerunfailures. I use python==3.11.0, pytest==7.2.1, pytest-rerunfailures==12.0. It also happens with pytest-rerunfailures==11.1.2. It causes big problems with my testrun.

Sample of code when teardown of fixture is not called when following test passes only after rerun:

import pytest

rerun_number = 0


@pytest.fixture(scope="class")
def class_fixture():
    print("Fixture class_fixture setup")
    yield
    print("Fixture class_fixture teardown")


class TestSuite1:
    def test_1(self, class_fixture):
        """Test 1"""
        print("Test 1")

    def test_2(self):
        """Test 1"""
        print("Test 1")

        # --- Rerun condition ---
        global rerun_number
        print(f"Rerun number = {rerun_number}")
        if rerun_number < 1:
            rerun_number += 1
            assert False
        # -----------------------


Log:

PS D:\autotests\git\tests> .\venv\Scripts\python.exe -X utf8 -m pytest -vv test_rerun_check3.py --reruns=1 --setup-show -s

test_rerun_check3.py::TestSuite1::test_1
SETUP    S base_url
SETUP    S _verify_url (fixtures used: base_url)
SETUP    S pytestconfig
SETUP    S delete_output_dir (fixtures used: pytestconfig)Fixture class_fixture setup

      SETUP    C class_fixture
        test_rerun_check3.py::TestSuite1::test_1 (fixtures used: _verify_url, base_url, class_fixture, delete_output_dir, pytestconfig, request)Test 1
PASSED
test_rerun_check3.py::TestSuite1::test_2
        test_rerun_check3.py::TestSuite1::test_2 (fixtures used: _verify_url, base_url, delete_output_dir, pytestconfig, request)Test 1
Rerun number = 0
RERUN
test_rerun_check3.py::TestSuite1::test_2
        test_rerun_check3.py::TestSuite1::test_2 (fixtures used: _verify_url, base_url, delete_output_dir, pytestconfig, request)Test 1
Rerun number = 1

TEARDOWN S delete_output_dir
TEARDOWN S pytestconfig
TEARDOWN S _verify_url
TEARDOWN S base_urlPASSED

Sample of code when teardown of fixture is not called when following test fails after rerun:

import pytest

rerun_number = 0


@pytest.fixture(scope="class")
def class_fixture():
    print("Fixture class_fixture setup")
    yield
    print("Fixture class_fixture teardown")


class TestSuite1:
    def test_1(self, class_fixture):
        """Test 1"""
        print("Test 1")

    def test_2(self):
        """Test 1"""
        print("Test 1")

        # --- Rerun condition ---
        global rerun_number
        print(f"Rerun number = {rerun_number}")
        if rerun_number < 2:
            rerun_number += 1
            assert False
        # -----------------------

Log:

PS D:\autotests\git\tests> .\venv\Scripts\python.exe -X utf8 -m pytest -vv test_rerun_check3.py --reruns=1 --setup-show -s

test_rerun_check3.py::TestSuite1::test_1
SETUP    S base_url
SETUP    S _verify_url (fixtures used: base_url)
SETUP    S pytestconfig
SETUP    S delete_output_dir (fixtures used: pytestconfig)Fixture class_fixture setup

      SETUP    C class_fixture
        test_rerun_check3.py::TestSuite1::test_1 (fixtures used: _verify_url, base_url, class_fixture, delete_output_dir, pytestconfig, request)Test 1
PASSED
test_rerun_check3.py::TestSuite1::test_2
        test_rerun_check3.py::TestSuite1::test_2 (fixtures used: _verify_url, base_url, delete_output_dir, pytestconfig, request)Test 1
Rerun number = 0
RERUN
test_rerun_check3.py::TestSuite1::test_2
        test_rerun_check3.py::TestSuite1::test_2 (fixtures used: _verify_url, base_url, delete_output_dir, pytestconfig, request)Test 1
Rerun number = 1

TEARDOWN S delete_output_dir
TEARDOWN S pytestconfig
TEARDOWN S _verify_url
TEARDOWN S base_urlFAILED

Sample of code without reruns and normal teardown:

import pytest

rerun_number = 0


@pytest.fixture(scope="class")
def class_fixture():
    print("Fixture class_fixture setup")
    yield
    print("Fixture class_fixture teardown")


class TestSuite1:
    def test_1(self, class_fixture):
        """Test 1"""
        print("Test 1")

    def test_2(self):
        """Test 1"""
        print("Test 1")

        # # --- Rerun condition ---
        # global rerun_number
        # print(f"Rerun number = {rerun_number}")
        # if rerun_number < 2:
        #     rerun_number += 1
        #     assert False
        # # -----------------------

Log without reruns:

PS D:\autotests\git\tests> .\venv\Scripts\python.exe -X utf8 -m pytest -vv test_rerun_check3.py --reruns=1 --setup-show -s

test_rerun_check3.py::TestSuite1::test_1
SETUP    S base_url
SETUP    S _verify_url (fixtures used: base_url)
SETUP    S pytestconfig
SETUP    S delete_output_dir (fixtures used: pytestconfig)Fixture class_fixture setup

      SETUP    C class_fixture
        test_rerun_check3.py::TestSuite1::test_1 (fixtures used: _verify_url, base_url, class_fixture, delete_output_dir, pytestconfig, request)Test 1
PASSED
test_rerun_check3.py::TestSuite1::test_2
        test_rerun_check3.py::TestSuite1::test_2 (fixtures used: _verify_url, base_url, delete_output_dir, pytestconfig, request)Test 1
Fixture class_fixture teardown

      TEARDOWN C class_fixture
TEARDOWN S delete_output_dir
TEARDOWN S pytestconfig
TEARDOWN S _verify_url
TEARDOWN S base_urlPASSED

Could you help me, is this problem related with this plugin?

@ExplorerOL ExplorerOL changed the title Teardown of fixture with scope=class not called if following test passes only after rerun or fails after rerun Teardown of fixture with scope=class is not called if following test passes only after rerun or fails after rerun Nov 9, 2023
@ExplorerOL
Copy link
Author

ExplorerOL commented Nov 10, 2023

In compatibilities I foud, that "This plugin may not be used with class, module, and package level fixtures"! I am really sad. I tried to modify code of plugin and managed to make it working with class-scoped fixture! My code may be not perfect and after reruns fixture with scope=class may called several times even in class, but in my case it is better than teardown is not called at all!

I modified a part of original code in the following way:

        if item in item.session._setupstate.stack:
            if PYTEST_GTE_63:
                # original code
                # for key in list(item.session._setupstate.stack.keys()):
                #     if key != item:
                #         del item.session._setupstate.stack[key]

                # my code
                keys_to_delete = list(item.session._setupstate.stack)[:-2]
                for key in keys_to_delete:
                    if key != item:
                        del item.session._setupstate.stack[key]

            else:
                for node in list(item.session._setupstate.stack):
                    if node != item:
                        item.session._setupstate.stack.remove(node)

@icemac icemac added the help wanted a pull request to fix this issue is welcome label Nov 10, 2023
@Mogost
Copy link

Mogost commented Nov 27, 2023

I can confirm this issue having researched one of the falls in my project.

HarryKrause added a commit to HarryKrause/pytest-rerunfailures that referenced this issue Feb 22, 2024
When using the only_rerun and rerun_except queries (or both), the
plug-in was removing the teardown operations from the call-stack before
checking to see if the test should be re-run. This resulted in the
stack having all fixture operations removed that did not correspond
to a function fixture.

This commit adds a private variable to each test item that keeps
track of whether a test encountered a terminal error. The plugin now
checks if a test has encountered a terminal error before attempting
to clear the stack.

This commit fixes:
- pytest-dev#241
- pytest-dev#234
HarryKrause added a commit to HarryKrause/pytest-rerunfailures that referenced this issue Feb 23, 2024
When using the only_rerun and rerun_except queries (or both), the
plug-in was removing the teardown operations from the call-stack before
checking to see if the test should be re-run. This resulted in the
stack having all fixture operations removed that did not correspond
to a function fixture.

This commit adds a private variable to each test item that keeps
track of whether a test encountered a terminal error. The plugin now
checks if a test has encountered a terminal error before attempting
to clear the stack.

This commit fixes:
- pytest-dev#241
- pytest-dev#234
icemac pushed a commit that referenced this issue Feb 29, 2024
When using the only_rerun and rerun_except queries (or both), the
plug-in was removing the teardown operations from the call-stack before
checking to see if the test should be re-run. This resulted in the
stack having all fixture operations removed that did not correspond
to a function fixture.

This commit adds a private variable to each test item that keeps
track of whether a test encountered a terminal error. The plugin now
checks if a test has encountered a terminal error before attempting
to clear the stack.

This commit fixes:
- #241
- #234
@ExplorerOL
Copy link
Author

ExplorerOL commented Mar 25, 2024

@icemac Hello! As I understand the incompatibility "This plugin may not be used with class, module, and package level fixtures" is overcome now. Am I right?

@icemac
Copy link
Contributor

icemac commented Mar 28, 2024

@ExplorerOL I think this is no longer a problem.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
help wanted a pull request to fix this issue is welcome
Projects
None yet
Development

Successfully merging a pull request may close this issue.

3 participants