Как правильно переопределить TestCase.assertEqual (), создавая правильную трассировку стека? - PullRequest
0 голосов
/ 03 февраля 2019

Я пытаюсь реализовать чистый ответ для Продолжаем в модульном тесте Python, когда утверждение не удается .

Я хотел бы улучшить ответ, получивший наибольшее количество голосов и не принятый: 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 делает?

...