在tearDown()方法中获取Python的unittest结果
在tearDown()方法中能否获取测试结果(也就是所有的检查是否都通过)?我在运行Selenium脚本,想在tearDown()里面做一些报告,但我不知道这样是否可行。
15 个回答
如果你看看 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 进行了修改,具体可以查看 这里)。
截至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以下)相关的内容,以及许多小的注释。
注意:我现在无法确认以下理论,因为我不在开发环境中。所以这可能只是个猜测。
你可以在你的 tearDown()
方法里检查 sys.exc_info()
的返回值。如果返回的是 (None, None, None)
,那就说明测试用例成功了。否则,你可以用返回的元组来查看异常对象的详细信息。
详细信息可以参考 sys.exc_info 的文档。
还有一种更明确的方法是写一个方法装饰器,这个装饰器可以加在所有需要特殊处理的测试用例方法上。这个装饰器可以拦截断言异常,并根据这些信息修改 self
的某些状态,让你的 tearDown
方法知道发生了什么。
@assertion_tracker
def test_foo(self):
# some test logic