mirror of https://github.com/python/cpython
bpo-44955: Always call stopTestRun() for implicitly created TestResult objects (GH-27831)
Method stopTestRun() is now always called in pair with method startTestRun() for TestResult objects implicitly created in TestCase.run(). Previously it was not called for test methods and classes decorated with a skipping decorator.
This commit is contained in:
parent
64f9e7b19d
commit
a9640d7553
|
@ -557,31 +557,30 @@ class TestCase(object):
|
||||||
function(*args, **kwargs)
|
function(*args, **kwargs)
|
||||||
|
|
||||||
def run(self, result=None):
|
def run(self, result=None):
|
||||||
orig_result = result
|
|
||||||
if result is None:
|
if result is None:
|
||||||
result = self.defaultTestResult()
|
result = self.defaultTestResult()
|
||||||
startTestRun = getattr(result, 'startTestRun', None)
|
startTestRun = getattr(result, 'startTestRun', None)
|
||||||
|
stopTestRun = getattr(result, 'stopTestRun', None)
|
||||||
if startTestRun is not None:
|
if startTestRun is not None:
|
||||||
startTestRun()
|
startTestRun()
|
||||||
|
else:
|
||||||
|
stopTestRun = None
|
||||||
|
|
||||||
result.startTest(self)
|
result.startTest(self)
|
||||||
|
try:
|
||||||
testMethod = getattr(self, self._testMethodName)
|
testMethod = getattr(self, self._testMethodName)
|
||||||
if (getattr(self.__class__, "__unittest_skip__", False) or
|
if (getattr(self.__class__, "__unittest_skip__", False) or
|
||||||
getattr(testMethod, "__unittest_skip__", False)):
|
getattr(testMethod, "__unittest_skip__", False)):
|
||||||
# If the class or method was skipped.
|
# If the class or method was skipped.
|
||||||
try:
|
|
||||||
skip_why = (getattr(self.__class__, '__unittest_skip_why__', '')
|
skip_why = (getattr(self.__class__, '__unittest_skip_why__', '')
|
||||||
or getattr(testMethod, '__unittest_skip_why__', ''))
|
or getattr(testMethod, '__unittest_skip_why__', ''))
|
||||||
self._addSkip(result, self, skip_why)
|
self._addSkip(result, self, skip_why)
|
||||||
finally:
|
|
||||||
result.stopTest(self)
|
|
||||||
return
|
return
|
||||||
expecting_failure_method = getattr(testMethod,
|
|
||||||
"__unittest_expecting_failure__", False)
|
expecting_failure = (
|
||||||
expecting_failure_class = getattr(self,
|
getattr(self, "__unittest_expecting_failure__", False) or
|
||||||
"__unittest_expecting_failure__", False)
|
getattr(testMethod, "__unittest_expecting_failure__", False)
|
||||||
expecting_failure = expecting_failure_class or expecting_failure_method
|
)
|
||||||
outcome = _Outcome(result)
|
outcome = _Outcome(result)
|
||||||
try:
|
try:
|
||||||
self._outcome = outcome
|
self._outcome = outcome
|
||||||
|
@ -610,12 +609,6 @@ class TestCase(object):
|
||||||
result.addSuccess(self)
|
result.addSuccess(self)
|
||||||
return result
|
return result
|
||||||
finally:
|
finally:
|
||||||
result.stopTest(self)
|
|
||||||
if orig_result is None:
|
|
||||||
stopTestRun = getattr(result, 'stopTestRun', None)
|
|
||||||
if stopTestRun is not None:
|
|
||||||
stopTestRun()
|
|
||||||
|
|
||||||
# explicitly break reference cycles:
|
# explicitly break reference cycles:
|
||||||
# outcome.errors -> frame -> outcome -> outcome.errors
|
# outcome.errors -> frame -> outcome -> outcome.errors
|
||||||
# outcome.expectedFailure -> frame -> outcome -> outcome.expectedFailure
|
# outcome.expectedFailure -> frame -> outcome -> outcome.expectedFailure
|
||||||
|
@ -625,6 +618,11 @@ class TestCase(object):
|
||||||
# clear the outcome, no more needed
|
# clear the outcome, no more needed
|
||||||
self._outcome = None
|
self._outcome = None
|
||||||
|
|
||||||
|
finally:
|
||||||
|
result.stopTest(self)
|
||||||
|
if stopTestRun is not None:
|
||||||
|
stopTestRun()
|
||||||
|
|
||||||
def doCleanups(self):
|
def doCleanups(self):
|
||||||
"""Execute all cleanup functions. Normally called for you after
|
"""Execute all cleanup functions. Normally called for you after
|
||||||
tearDown."""
|
tearDown."""
|
||||||
|
|
|
@ -7,6 +7,8 @@ class Test_TestSkipping(unittest.TestCase):
|
||||||
|
|
||||||
def test_skipping(self):
|
def test_skipping(self):
|
||||||
class Foo(unittest.TestCase):
|
class Foo(unittest.TestCase):
|
||||||
|
def defaultTestResult(self):
|
||||||
|
return LoggingResult(events)
|
||||||
def test_skip_me(self):
|
def test_skip_me(self):
|
||||||
self.skipTest("skip")
|
self.skipTest("skip")
|
||||||
events = []
|
events = []
|
||||||
|
@ -16,8 +18,15 @@ class Test_TestSkipping(unittest.TestCase):
|
||||||
self.assertEqual(events, ['startTest', 'addSkip', 'stopTest'])
|
self.assertEqual(events, ['startTest', 'addSkip', 'stopTest'])
|
||||||
self.assertEqual(result.skipped, [(test, "skip")])
|
self.assertEqual(result.skipped, [(test, "skip")])
|
||||||
|
|
||||||
|
events = []
|
||||||
|
test.run()
|
||||||
|
self.assertEqual(events, ['startTestRun', 'startTest', 'addSkip',
|
||||||
|
'stopTest', 'stopTestRun'])
|
||||||
|
|
||||||
# Try letting setUp skip the test now.
|
# Try letting setUp skip the test now.
|
||||||
class Foo(unittest.TestCase):
|
class Foo(unittest.TestCase):
|
||||||
|
def defaultTestResult(self):
|
||||||
|
return LoggingResult(events)
|
||||||
def setUp(self):
|
def setUp(self):
|
||||||
self.skipTest("testing")
|
self.skipTest("testing")
|
||||||
def test_nothing(self): pass
|
def test_nothing(self): pass
|
||||||
|
@ -29,8 +38,15 @@ class Test_TestSkipping(unittest.TestCase):
|
||||||
self.assertEqual(result.skipped, [(test, "testing")])
|
self.assertEqual(result.skipped, [(test, "testing")])
|
||||||
self.assertEqual(result.testsRun, 1)
|
self.assertEqual(result.testsRun, 1)
|
||||||
|
|
||||||
|
events = []
|
||||||
|
test.run()
|
||||||
|
self.assertEqual(events, ['startTestRun', 'startTest', 'addSkip',
|
||||||
|
'stopTest', 'stopTestRun'])
|
||||||
|
|
||||||
def test_skipping_subtests(self):
|
def test_skipping_subtests(self):
|
||||||
class Foo(unittest.TestCase):
|
class Foo(unittest.TestCase):
|
||||||
|
def defaultTestResult(self):
|
||||||
|
return LoggingResult(events)
|
||||||
def test_skip_me(self):
|
def test_skip_me(self):
|
||||||
with self.subTest(a=1):
|
with self.subTest(a=1):
|
||||||
with self.subTest(b=2):
|
with self.subTest(b=2):
|
||||||
|
@ -54,11 +70,20 @@ class Test_TestSkipping(unittest.TestCase):
|
||||||
self.assertIsNot(subtest, test)
|
self.assertIsNot(subtest, test)
|
||||||
self.assertEqual(result.skipped[2], (test, "skip 3"))
|
self.assertEqual(result.skipped[2], (test, "skip 3"))
|
||||||
|
|
||||||
|
events = []
|
||||||
|
test.run()
|
||||||
|
self.assertEqual(events,
|
||||||
|
['startTestRun', 'startTest', 'addSkip', 'addSkip',
|
||||||
|
'addSkip', 'stopTest', 'stopTestRun'])
|
||||||
|
|
||||||
def test_skipping_decorators(self):
|
def test_skipping_decorators(self):
|
||||||
op_table = ((unittest.skipUnless, False, True),
|
op_table = ((unittest.skipUnless, False, True),
|
||||||
(unittest.skipIf, True, False))
|
(unittest.skipIf, True, False))
|
||||||
for deco, do_skip, dont_skip in op_table:
|
for deco, do_skip, dont_skip in op_table:
|
||||||
class Foo(unittest.TestCase):
|
class Foo(unittest.TestCase):
|
||||||
|
def defaultTestResult(self):
|
||||||
|
return LoggingResult(events)
|
||||||
|
|
||||||
@deco(do_skip, "testing")
|
@deco(do_skip, "testing")
|
||||||
def test_skip(self): pass
|
def test_skip(self): pass
|
||||||
|
|
||||||
|
@ -66,6 +91,7 @@ class Test_TestSkipping(unittest.TestCase):
|
||||||
def test_dont_skip(self): pass
|
def test_dont_skip(self): pass
|
||||||
test_do_skip = Foo("test_skip")
|
test_do_skip = Foo("test_skip")
|
||||||
test_dont_skip = Foo("test_dont_skip")
|
test_dont_skip = Foo("test_dont_skip")
|
||||||
|
|
||||||
suite = unittest.TestSuite([test_do_skip, test_dont_skip])
|
suite = unittest.TestSuite([test_do_skip, test_dont_skip])
|
||||||
events = []
|
events = []
|
||||||
result = LoggingResult(events)
|
result = LoggingResult(events)
|
||||||
|
@ -78,19 +104,41 @@ class Test_TestSkipping(unittest.TestCase):
|
||||||
self.assertEqual(result.skipped, [(test_do_skip, "testing")])
|
self.assertEqual(result.skipped, [(test_do_skip, "testing")])
|
||||||
self.assertTrue(result.wasSuccessful())
|
self.assertTrue(result.wasSuccessful())
|
||||||
|
|
||||||
|
events = []
|
||||||
|
test_do_skip.run()
|
||||||
|
self.assertEqual(len(result.skipped), 1)
|
||||||
|
self.assertEqual(events, ['startTestRun', 'startTest', 'addSkip',
|
||||||
|
'stopTest', 'stopTestRun'])
|
||||||
|
|
||||||
|
events = []
|
||||||
|
test_dont_skip.run()
|
||||||
|
self.assertEqual(len(result.skipped), 1)
|
||||||
|
self.assertEqual(events, ['startTestRun', 'startTest', 'addSuccess',
|
||||||
|
'stopTest', 'stopTestRun'])
|
||||||
|
|
||||||
def test_skip_class(self):
|
def test_skip_class(self):
|
||||||
@unittest.skip("testing")
|
@unittest.skip("testing")
|
||||||
class Foo(unittest.TestCase):
|
class Foo(unittest.TestCase):
|
||||||
|
def defaultTestResult(self):
|
||||||
|
return LoggingResult(events)
|
||||||
def test_1(self):
|
def test_1(self):
|
||||||
record.append(1)
|
record.append(1)
|
||||||
|
events = []
|
||||||
record = []
|
record = []
|
||||||
result = unittest.TestResult()
|
result = LoggingResult(events)
|
||||||
test = Foo("test_1")
|
test = Foo("test_1")
|
||||||
suite = unittest.TestSuite([test])
|
suite = unittest.TestSuite([test])
|
||||||
suite.run(result)
|
suite.run(result)
|
||||||
|
self.assertEqual(events, ['startTest', 'addSkip', 'stopTest'])
|
||||||
self.assertEqual(result.skipped, [(test, "testing")])
|
self.assertEqual(result.skipped, [(test, "testing")])
|
||||||
self.assertEqual(record, [])
|
self.assertEqual(record, [])
|
||||||
|
|
||||||
|
events = []
|
||||||
|
test.run()
|
||||||
|
self.assertEqual(events, ['startTestRun', 'startTest', 'addSkip',
|
||||||
|
'stopTest', 'stopTestRun'])
|
||||||
|
self.assertEqual(record, [])
|
||||||
|
|
||||||
def test_skip_non_unittest_class(self):
|
def test_skip_non_unittest_class(self):
|
||||||
@unittest.skip("testing")
|
@unittest.skip("testing")
|
||||||
class Mixin:
|
class Mixin:
|
||||||
|
|
|
@ -0,0 +1,5 @@
|
||||||
|
Method :meth:`~unittest.TestResult.stopTestRun` is now always called in pair
|
||||||
|
with method :meth:`~unittest.TestResult.startTestRun` for
|
||||||
|
:class:`~unittest.TestResult` objects implicitly created in
|
||||||
|
:meth:`~unittest.TestCase.run`. Previously it was not called for test
|
||||||
|
methods and classes decorated with a skipping decorator.
|
Loading…
Reference in New Issue