在tearDown()方法中获取Python的unittest结果

76 投票
15 回答
54331 浏览
提问于 2025-04-16 08:22

在tearDown()方法中能否获取测试结果(也就是所有的检查是否都通过)?我在运行Selenium脚本,想在tearDown()里面做一些报告,但我不知道这样是否可行。

15 个回答

41

如果你看看 unittest.TestCase.run 的实现,你会发现所有的测试结果都被收集在一个结果对象里(通常是一个 unittest.TestResult 的实例),这个对象是作为参数传进去的。也就是说,测试结果的状态并没有保留在 unittest.TestCase 对象里。

所以在 unittest.TestCase.tearDown 方法里,你能做的事情不多,除非你不顾一切地打破测试用例和测试结果之间优雅的分离,比如这样做:

import unittest

class MyTest(unittest.TestCase):

    currentResult = None # Holds last result object passed to run method

    def setUp(self):
        pass

    def tearDown(self):
        ok = self.currentResult.wasSuccessful()
        errors = self.currentResult.errors
        failures = self.currentResult.failures
        print ' All tests passed so far!' if ok else \
                ' %d errors and %d failures so far' % \
                (len(errors), len(failures))

    def run(self, result=None):
        self.currentResult = result # Remember result for use in tearDown
        unittest.TestCase.run(self, result) # call superclass run method

    def test_onePlusOneEqualsTwo(self):
        self.assertTrue(1 + 1 == 2) # Succeeds

    def test_onePlusOneEqualsThree(self):
        self.assertTrue(1 + 1 == 3) # Fails

    def test_onePlusNoneIsNone(self):
        self.assertTrue(1 + None is None) # Raises TypeError

if __name__ == '__main__':
    unittest.main()

这个方法适用于 Python 2.6 到 3.3(针对新版本的 Python 进行了修改,具体可以查看 这里)。

67

截至2022年3月,这个回答更新了,以支持Python 3.4到3.11版本(包括最新的开发版本)。错误或失败的分类和unittest输出中使用的分类是一样的。在tearDown()之前的代码不需要做任何修改就可以正常工作。它可以正确识别装饰器skipIf()expectedFailure。同时,它也兼容pytest

代码:

import unittest

class MyTest(unittest.TestCase):
    def tearDown(self):
        if hasattr(self._outcome, 'errors'):
            # Python 3.4 - 3.10  (These two methods have no side effects)
            result = self.defaultTestResult()
            self._feedErrorsToResult(result, self._outcome.errors)
        else:
            # Python 3.11+
            result = self._outcome.result
        ok = all(test != self for test, text in result.errors + result.failures)

        # Demo output:  (print short info immediately - not important)
        if ok:
            print('\nOK: %s' % (self.id(),))
        for typ, errors in (('ERROR', result.errors), ('FAIL', result.failures)):
            for test, text in errors:
                if test is self:
                    #  the full traceback is in the variable `text`
                    msg = [x for x in text.split('\n')[1:]
                           if not x.startswith(' ')][0]
                    print("\n\n%s: %s\n     %s" % (typ, self.id(), msg))

如果你不需要异常信息,那么后半部分可以去掉。如果你还想要追踪信息,就用整个变量text,而不是msg。它唯一无法识别的是在expectedFailure块中出现的意外成功。

示例测试方法:

    def test_error(self):
        self.assertEqual(1 / 0, 1)

    def test_fail(self):
        self.assertEqual(2, 1)

    def test_success(self):
        self.assertEqual(1, 1)

示例输出:

$ python3 -m unittest test

ERROR: q.MyTest.test_error
     ZeroDivisionError: division by zero
E

FAIL: q.MyTest.test_fail
     AssertionError: 2 != 1
F

OK: q.MyTest.test_success
.
======================================================================
... skipped the usual output from unittest with tracebacks ...
...
Ran 3 tests in 0.001s

FAILED (failures=1, errors=1)

完整代码,包括expectedFailure装饰器的示例。

编辑:当我把这个解决方案更新到Python 3.11时,删除了所有与旧版Python(3.4以下)相关的内容,以及许多小的注释。

13

注意:我现在无法确认以下理论,因为我不在开发环境中。所以这可能只是个猜测。

你可以在你的 tearDown() 方法里检查 sys.exc_info() 的返回值。如果返回的是 (None, None, None),那就说明测试用例成功了。否则,你可以用返回的元组来查看异常对象的详细信息。

详细信息可以参考 sys.exc_info 的文档。

还有一种更明确的方法是写一个方法装饰器,这个装饰器可以加在所有需要特殊处理的测试用例方法上。这个装饰器可以拦截断言异常,并根据这些信息修改 self 的某些状态,让你的 tearDown 方法知道发生了什么。

@assertion_tracker
def test_foo(self):
    # some test logic

撰写回答