I am trying to implement a clean answer for Continuing in Python's unittest when an assertion fails.
I would like the improve the most voted and non-accepted answer: https://stackoverflow.com/a/5028110/4934640
The problem I have this answer, is that it forces-me to use try...catch
inside my Unit Tests. Then, I am trying to encapsulate the try...catch
logic in an override of the TestCase.assertEqual
method.
So far, so good. I manage to override the TestCase.assertEqual
method, however, the stacktrace presented is wrong.
Running the following minimal example:
import unittest
import traceback
class MultipleAssertionFailures(unittest.TestCase):
def __init__(self, *args, **kwargs):
self.verificationErrors = []
super(MultipleAssertionFailures, self).__init__( *args, **kwargs )
def tearDown(self):
super(MultipleAssertionFailures, self).tearDown()
if self.verificationErrors:
self.fail( '\n\n' + '\n'.join( self.verificationErrors ) )
self.verificationErrors.clear()
def assertEqual(self, goal, results, msg=""):
try:
super( MultipleAssertionFailures, self ).assertEqual( goal, results, msg )
except unittest.TestCase.failureException as error:
stacktrace = traceback.format_exc()
# stacktrace = traceback.format_stack()
self.verificationErrors.append( "".join( stacktrace ) )
# class DummyTestCase(unittest.TestCase):
class DummyTestCase(MultipleAssertionFailures):
def setUp(self):
self.maxDiff = None
super(DummyTestCase, self).setUp()
def tearDown(self):
super(DummyTestCase, self).tearDown()
def test_function_name(self):
self.assertEqual( "var", "bar" )
self.assertEqual( "1937", "511" )
if __name__ == '__main__':
unittest.main()
You got the following output:
F
======================================================================
FAIL: test_function_name (__main__.DummyTestCase)
----------------------------------------------------------------------
Traceback (most recent call last):
File "D:\User\Downloads\test.py", line 36, in tearDown
super(DummyTestCase, self).tearDown()
File "D:\User\Downloads\test.py", line 15, in tearDown
self.fail( '\n\n' + '\n'.join( self.verificationErrors ) )
AssertionError:
Traceback (most recent call last):
File "D:\User\Downloads\test.py", line 21, in assertEqual
super( MultipleAssertionFailures, self ).assertEqual( goal, results, msg )
File "F:\Python\lib\unittest\case.py", line 844, in assertEqual
assertion_func(first, second, msg=msg)
File "F:\Python\lib\unittest\case.py", line 1228, in assertMultiLineEqual
self.fail(self._formatMessage(msg, standardMsg))
File "F:\Python\lib\unittest\case.py", line 685, in fail
raise self.failureException(msg)
AssertionError: 'var' != 'bar'
- var
? ^
+ bar
? ^
:
Traceback (most recent call last):
File "D:\User\Downloads\test.py", line 21, in assertEqual
super( MultipleAssertionFailures, self ).assertEqual( goal, results, msg )
File "F:\Python\lib\unittest\case.py", line 844, in assertEqual
assertion_func(first, second, msg=msg)
File "F:\Python\lib\unittest\case.py", line 1228, in assertMultiLineEqual
self.fail(self._formatMessage(msg, standardMsg))
File "F:\Python\lib\unittest\case.py", line 685, in fail
raise self.failureException(msg)
AssertionError: '1937' != '511'
- 1937
+ 511
:
On the output, you will noticed the two presented stacktraces are wrong because they do not reflect where the Unit Tests code are, but only where the my assertion code is on. By commenting the class DummyTestCase(MultipleAssertionFailures)
and uncommenting the class DummyTestCase(unittest.TestCase)
on the minimal example, you will see how the correctly output of the stacktrace should be:
F
======================================================================
FAIL: test_function_name (__main__.DummyTestCase)
----------------------------------------------------------------------
Traceback (most recent call last):
File "D:\User\Downloads\test.py", line 39, in test_function_name
self.assertEqual( "var", "bar" )
AssertionError: 'var' != 'bar'
- var
? ^
+ bar
? ^
This stacktrace (the builtin one), correctly points to the test.py", line 39, in test_function_name
. I tried looking over the TestCase
module, but I could not figure out how it did to manage to produce this meaningful stacktrace.
Alternatively, uncommenting the line stacktrace = traceback.format_exc()
, we can get a really full stacktrace:
F
======================================================================
FAIL: test_function_name (__main__.DummyTestCase)
----------------------------------------------------------------------
Traceback (most recent call last):
File "D:\User\Downloads\test.py", line 36, in tearDown
super(DummyTestCase, self).tearDown()
File "D:\User\Downloads\test.py", line 15, in tearDown
self.fail( '\n\n' + '\n'.join( self.verificationErrors ) )
AssertionError:
File "D:\User\Downloads\test.py", line 43, in <module>
unittest.main()
File "F:\Python\lib\unittest\main.py", line 101, in __init__
self.runTests()
File "F:\Python\lib\unittest\main.py", line 271, in runTests
self.result = testRunner.run(self.test)
File "F:\Python\lib\unittest\runner.py", line 176, in run
test(result)
File "F:\Python\lib\unittest\suite.py", line 84, in __call__
return self.run(*args, **kwds)
File "F:\Python\lib\unittest\suite.py", line 122, in run
test(result)
File "F:\Python\lib\unittest\suite.py", line 84, in __call__
return self.run(*args, **kwds)
File "F:\Python\lib\unittest\suite.py", line 122, in run
test(result)
File "F:\Python\lib\unittest\case.py", line 668, in __call__
return self.run(*args, **kwds)
File "F:\Python\lib\unittest\case.py", line 620, in run
testMethod()
File "D:\User\Downloads\test.py", line 39, in test_function_name
self.assertEqual( "var", "bar" )
File "D:\User\Downloads\test.py", line 25, in assertEqual
stacktrace = traceback.format_stack()
File "D:\User\Downloads\test.py", line 43, in <module>
unittest.main()
File "F:\Python\lib\unittest\main.py", line 101, in __init__
self.runTests()
File "F:\Python\lib\unittest\main.py", line 271, in runTests
self.result = testRunner.run(self.test)
File "F:\Python\lib\unittest\runner.py", line 176, in run
test(result)
File "F:\Python\lib\unittest\suite.py", line 84, in __call__
return self.run(*args, **kwds)
File "F:\Python\lib\unittest\suite.py", line 122, in run
test(result)
File "F:\Python\lib\unittest\suite.py", line 84, in __call__
return self.run(*args, **kwds)
File "F:\Python\lib\unittest\suite.py", line 122, in run
test(result)
File "F:\Python\lib\unittest\case.py", line 668, in __call__
return self.run(*args, **kwds)
File "F:\Python\lib\unittest\case.py", line 620, in run
testMethod()
File "D:\User\Downloads\test.py", line 40, in test_function_name
self.assertEqual( "1937", "511" )
File "D:\User\Downloads\test.py", line 25, in assertEqual
stacktrace = traceback.format_stack()
How could I always cut off this full stacktrace into something meaningful as the builtin TestCase.assertEqual
does?