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

unittest: fix the _GetOutOf_testPartExecutor exception kind. Fixes … #6972

Closed
wants to merge 1 commit into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
1 change: 1 addition & 0 deletions AUTHORS
Expand Up @@ -220,6 +220,7 @@ Pieter Mulder
Piotr Banaszkiewicz
Pulkit Goyal
Punyashloka Biswal
Quentin Bey
Quentin Pradet
Ralf Schmitt
Ralph Giles
Expand Down
1 change: 1 addition & 0 deletions changelog/6947.bugfix.rst
@@ -0,0 +1 @@
Unittest cleanup functions are called even if a test fails.
8 changes: 2 additions & 6 deletions src/_pytest/unittest.py
Expand Up @@ -113,15 +113,12 @@ class TestCaseFunction(Function):
_testcase = None

def setup(self):
self._needs_explicit_tearDown = False
self._testcase = self.parent.obj(self.name)
self._obj = getattr(self._testcase, self.name)
if hasattr(self, "_request"):
self._request._fillfixtures()

def teardown(self):
if self._needs_explicit_tearDown:
self._testcase.tearDown()
self._testcase = None
self._obj = None

Expand Down Expand Up @@ -209,7 +206,7 @@ def runtest(self):

testMethod = getattr(self._testcase, self._testcase._testMethodName)

class _GetOutOf_testPartExecutor(KeyboardInterrupt):
class _GetOutOf_testPartExecutor(Exception):
"""Helper exception to get out of unittests's testPartExecutor (see TestCase.run)."""

@functools.wraps(testMethod)
Expand All @@ -218,13 +215,12 @@ def wrapped_testMethod(*args, **kwargs):
features can have a chance to kick in (notably --pdb)"""
try:
self.ihook.pytest_pyfunc_call(pyfuncitem=self)
except unittest.SkipTest:
except (unittest.SkipTest, exit.Exception):
raise
except Exception as exc:
expecting_failure = self._expecting_failure(testMethod)
if expecting_failure:
raise
self._needs_explicit_tearDown = True
raise _GetOutOf_testPartExecutor(exc)

setattr(self._testcase, self._testcase._testMethodName, wrapped_testMethod)
Expand Down
85 changes: 85 additions & 0 deletions testing/test_unittest.py
Expand Up @@ -209,6 +209,91 @@ def test_demo(self):
assert type(obj).__name__ != "TestCaseObjectsShouldBeCleanedUp"


def test_cleanup_called_issue6947(testdir):
"""
Are unittest.TestCase cleanup functions not invoked on test failure?
When a test fails and the cleanup is not called, all following tests may fail.
This is truly visible for test using a database when the transaction is not rolled back at the end.
"""
testpath = testdir.makepyfile(
"""
import unittest
import pytest

def increment_cleanup_count():
TestCaseObjectsShouldBeCleanedUp.cleanup_counter += 1

class TestCaseObjectsShouldBeCleanedUp(unittest.TestCase):
cleanup_counter = 0
def setUp(self):
self.addCleanup(increment_cleanup_count)
def test_1_goes_well(self):
# First test to be run
assert TestCaseObjectsShouldBeCleanedUp.cleanup_counter == 0
def test_2_that_fails(self):
# Second test to be run, should fail
assert False
def test_3_goes_well_after_failure(self):
# Third test to be run
assert TestCaseObjectsShouldBeCleanedUp.cleanup_counter == 2
def test_4_exit(self):
pytest.exit("pytest_exit called")
def test_5_not_run(self):
assert True
"""
)
reprec = testdir.inline_run(testpath)
passed, skipped, failed = reprec.countoutcomes()
# test_1_goes_well passed
# test_2_that_fails failed
# test_3_goes_well_after_failure passed
# and finally test_4 quit
assert failed == 1, failed
assert passed == 2
assert passed + skipped + failed == 3


def test_teardown_called_issue6947(testdir):
"""
This test assert the TestCase.tearDown method is called once after every tests.

Looks very similar to test_cleanup_called_issue6947 but testing a different mechanism.
"""
testpath = testdir.makepyfile(
"""
import unittest
import pytest

class TestCaseObjectsShouldBeCleanedUp(unittest.TestCase):
cleanup_counter = 0
def tearDown(self):
TestCaseObjectsShouldBeCleanedUp.cleanup_counter += 1
def test_1_goes_well(self):
# First test to be run
assert TestCaseObjectsShouldBeCleanedUp.cleanup_counter == 0
def test_2_that_fails(self):
# Second test to be run, should fail
assert False
def test_3_goes_well_after_failure(self):
# Third test to be run
assert TestCaseObjectsShouldBeCleanedUp.cleanup_counter == 2
def test_4_exit(self):
pytest.exit("pytest_exit called")
def test_5_not_run(self):
assert True
"""
)
reprec = testdir.inline_run(testpath)
passed, skipped, failed = reprec.countoutcomes()
# test_1_goes_well passed
# test_2_that_fails failed
# test_3_goes_well_after_failure passed
# and finally test_4 quit
assert failed == 1, failed
assert passed == 2
assert passed + skipped + failed == 3


def test_unittest_skip_issue148(testdir):
testpath = testdir.makepyfile(
"""
Expand Down