Python 单元测试:使 AssertionError 成为错误而不是失败

Python unittest: make AssertionError an error instead of a failure

Python 2.7。 The unittest doc 说:

To make migrating existing test suites easier, unittest supports tests raising AssertionError to indicate test failure. However, it is recommended that you use the explicit TestCase.fail*() and TestCase.assert*() methods instead, as future versions of unittest may treat AssertionError differently.

我在测试代码中使用了很多 assert 语句,但这些断言的失败实际上应该是测试错误(即 "code did not run properly with those inputs")而不是失败(即 "code gave incorrect output").

我可以看到以下可能的解决方案:

  1. 重写测试代码以抛出更好类型的异常
  2. 将除测试断言本身 (self.assertSomething(...)) 之外的所有测试方法包装在 try...except AssertionError: raise SomeOtherException 块中
  3. 更改单元测试的行为,使其考虑这些错误而不是失败。

选项 1 会花费一些时间,选项 2 感觉很老套;选项 3 对我来说是最好的,但它可用吗? (以防万一:不,我不能切换到 Python 3.)我在网上看不到任何东西,但很难使用特定的关键字。

MWE:

import unittest


def add_one_to_int(a):
    assert isinstance(a, int)
    return a + 1


class TestAddOne(unittest.TestCase):
    def test_one_plus_one_is_three(self):
        # This tests fails with
        #   AssertionError: 2 != 3
        # which is fine
        self.assertEqual(add_one_to_int(1), 3)  

    def test_add_one_to_str(self):
        # This tests fails with
        #   AssertionError
        # when I would rather have it an error
        add_one_to_int('some string')

if __name__ == '__main__':
    unittest.main(verbosity=2)  # 2 failures instead of 1 failure, 1 error

我认为选项 3 可以通过 class 属性 "failureException" 实现,正如 Python 2.7:

的单元测试文档中所定义的

failureException: This class attribute gives the exception raised by the test method. If a test framework needs to use a specialized exception, possibly to carry additional information, it must subclass this exception in order to “play fair” with the framework. The initial value of this attribute is AssertionError.

例如:

import unittest

class MyException(Exception): 
    pass

class MyUnitTest(unittest.TestCase):
    failureException = MyException

def add_one_to_int(a):
    assert isinstance(a, int)
    return a + 1


class TestAddOne(MyUnitTest):  # <--------- See above
    def test_one_plus_one_is_three(self):
        # This tests fails with
        #   AssertionError: 2 != 3
        # which is fine
        self.assertEqual(add_one_to_int(1), 3)  

    def test_add_one_to_str(self):
        # This tests fails with
        #   AssertionError
        # when I would rather have it an error
        add_one_to_int('some string')

if __name__ == '__main__':
    unittest.main(verbosity=2)  # ------> FAILED (failures=1, errors=1)