首页 > 解决方案 > 如何正确覆盖 TestCase.assertEqual(),产生正确的堆栈跟踪?

问题描述

我正在尝试为Continuing in Python's unittest when an assertion failed实现一个干净的答案。

我想改进投票最多和不被接受的答案:https ://stackoverflow.com/a/5028110/4934640

我有这个答案的问题是它迫使我try...catch在我的单元测试中使用。然后,我试图将try...catch逻辑封装在方法的覆盖中TestCase.assertEqual

到目前为止,一切都很好。我设法覆盖了该TestCase.assertEqual方法,但是,提供的堆栈跟踪是错误的。

运行以下最小示例:

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()

你得到以下输出:

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
 : 

在输出中,您会注意到两个显示的堆栈跟踪是错误的,因为它们不反映单元测试代码的位置,而只反映我的断言代码所在的位置。通过在最小示例上注释class DummyTestCase(MultipleAssertionFailures)和取消注释class DummyTestCase(unittest.TestCase),您将看到堆栈跟踪的正确输出应该如何:

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
? ^

此堆栈跟踪(内置堆栈跟踪)正确指向test.py", line 39, in test_function_name. 我尝试查看TestCase 模块,但我无法弄清楚它是如何设法产生这个有意义的堆栈跟踪的。

或者,取消注释该行stacktrace = traceback.format_exc(),我们可以获得一个非常完整的堆栈跟踪:

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()

我怎么能总是把这个完整的堆栈跟踪切断成像内置TestCase.assertEqual函数那样有意义的东西?

标签: pythonpython-3.xpython-2.7unit-testingstack-trace

解决方案


推荐阅读