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

是否有可能在 tearDown ()方法中获得测试结果(即是否所有断言都通过了) ?我正在运行 Selenium 脚本,我想从 tearDown ()内部做一些报告,但是我不知道这是否可行。

49063 次浏览

警告: 我现在没有办法重复检查下面的理论,因为我离开了开发箱。所以这可能是瞎猜的。

也许您可以在 tearDown ()方法中检查 sys.exc_info()的返回值,如果它返回 (None, None, None),您就知道测试用例成功了。否则,可以使用返回的元组来查询异常对象。

请参阅 Sys.exc _ info文档。

另一种更加明确的方法是编写一个方法装饰器,您可以将它装饰到需要这种特殊处理的所有测试用例方法上。这个装饰器可以拦截断言异常,并基于此修改 self中的某些状态,从而允许 tearDown 方法了解发生了什么。

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

如果查看 unittest.TestCase.run的实现,可以看到所有测试结果都收集在作为参数传递的 result 对象(通常是 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 下面进行了修改)。

如果你正在使用 Python 2,你可以使用方法 _resultForDoCleanups。这个方法返回一个 TextTestResult对象:

<unittest.runner.TextTestResult run=1 errors=0 failures=0>

您可以使用此对象检查测试结果:

def tearDown(self):
if self._resultForDoCleanups.failures:
...
elif self._resultForDoCleanups.errors:
...
else:
# Success

如果你正在使用 Python 3,你可以使用 _outcomeForDoCleanups:

def tearDown(self):
if not self._outcomeForDoCleanups.success:
...

接下来是 Amatellanes 的回答,如果你使用的是 Python 3.4,你就不能使用 _outcomeForDoCleanups:

def _test_has_failed(self):
for method, error in self._outcome.errors:
if error:
return True
return False

这是令人作呕,但它似乎工作。

这取决于你想做什么样的报道。

如果您希望在出现故障时执行某些操作(例如 生成一个截图) ,可以通过重写 failureException来实现,而不是使用 tearDown()

例如:

@property
def failureException(self):
class MyFailureException(AssertionError):
def __init__(self_, *args, **kwargs):
screenshot_dir = 'reports/screenshots'
if not os.path.exists(screenshot_dir):
os.makedirs(screenshot_dir)
self.driver.save_screenshot('{0}/{1}.png'.format(screenshot_dir, self.id()))
return super(MyFailureException, self_).__init__(*args, **kwargs)
MyFailureException.__name__ = AssertionError.__name__
return MyFailureException

Python 2.7.

您还可以在 unittest.main ()之后获取 结果:

t = unittest.main(exit=False)
print t.result

或使用 套房:

suite.addTests(tests)
result = unittest.result.TestResult()
suite.run(result)
print result

可以使用 Unittest. TestCase.id ()方法检索当前测试的名称。

该示例显示如何:

  • 查找当前测试在错误或故障列表中是否有错误或失败
  • 打印测试标识,并注明通过、失败或例外

这里测试的示例适用于 Scopey 就是个很好的例子

def tearDown(self):
result = "PASS"
#### Find and show result for current test
# I did not find any nicer/neater way of comparing self.id() with test id stored in errors or failures lists :-7
id = str(self.id()).split('.')[-1]
# id() e.g. tup[0]:<__main__.MyTest testMethod=test_onePlusNoneIsNone>
#           str(tup[0]):"test_onePlusOneEqualsThree (__main__.MyTest)"
#           str(self.id()) = __main__.MyTest.test_onePlusNoneIsNone
for tup in self.currentResult.failures:
if str(tup[0]).startswith(id):
print ' test %s failure:%s' % (self.id(), tup[1])
## DO TEST FAIL ACTION HERE
result = "FAIL"
for tup in self.currentResult.errors:
if str(tup[0]).startswith(id):
print ' test %s error:%s' % (self.id(), tup[1])
## DO TEST EXCEPTION ACTION HERE
result = "EXCEPTION"


print "Test:%s Result:%s" % (self.id(), result)

结果例子:

python run_scripts/tut2.py 2>&1
E test __main__.MyTest.test_onePlusNoneIsNone error:Traceback (most recent call last):
File "run_scripts/tut2.py", line 80, in test_onePlusNoneIsNone
self.assertTrue(1 + None is None) # raises TypeError
TypeError: unsupported operand type(s) for +: 'int' and 'NoneType'


Test:__main__.MyTest.test_onePlusNoneIsNone Result:EXCEPTION
F test __main__.MyTest.test_onePlusOneEqualsThree failure:Traceback (most recent call last):
File "run_scripts/tut2.py", line 77, in test_onePlusOneEqualsThree
self.assertTrue(1 + 1 == 3) # fails
AssertionError: False is not true


Test:__main__.MyTest.test_onePlusOneEqualsThree Result:FAIL
Test:__main__.MyTest.test_onePlusOneEqualsTwo Result:PASS
.
======================================================================
ERROR: test_onePlusNoneIsNone (__main__.MyTest)
----------------------------------------------------------------------
Traceback (most recent call last):
File "run_scripts/tut2.py", line 80, in test_onePlusNoneIsNone
self.assertTrue(1 + None is None) # raises TypeError
TypeError: unsupported operand type(s) for +: 'int' and 'NoneType'


======================================================================
FAIL: test_onePlusOneEqualsThree (__main__.MyTest)
----------------------------------------------------------------------
Traceback (most recent call last):
File "run_scripts/tut2.py", line 77, in test_onePlusOneEqualsThree
self.assertTrue(1 + 1 == 3) # fails
AssertionError: False is not true


----------------------------------------------------------------------
Ran 3 tests in 0.001s


FAILED (failures=1, errors=1)

受到 Scopey 的回答的启发,我决定将 无情提升到下一个层次,并提出了以下建议。

它既可以在普通的 unittest 中工作,也可以通过 nosettest 运行,还可以在 Python 版本2.7、3.2、3.3和3.4中工作(我没有特别测试3.0、3.1或3.5,因为我目前还没有安装它们,但是如果我正确地阅读了 源代码,它应该也可以在3.5中工作) :

#! /usr/bin/env python


from __future__ import unicode_literals
import logging
import os
import sys
import unittest




# Log file to see squawks during testing
formatter = logging.Formatter(fmt='%(levelname)-8s %(name)s: %(message)s')
log_file = os.path.splitext(os.path.abspath(__file__))[0] + '.log'
handler = logging.FileHandler(log_file)
handler.setFormatter(formatter)
logging.root.addHandler(handler)
logging.root.setLevel(logging.DEBUG)
log = logging.getLogger(__name__)




PY = tuple(sys.version_info)[:3]




class SmartTestCase(unittest.TestCase):


"""Knows its state (pass/fail/error) by the time its tearDown is called."""


def run(self, result):
# Store the result on the class so tearDown can behave appropriately
self.result = result.result if hasattr(result, 'result') else result
if PY >= (3, 4, 0):
self._feedErrorsToResultEarly = self._feedErrorsToResult
self._feedErrorsToResult = lambda *args, **kwargs: None  # no-op
super(SmartTestCase, self).run(result)


@property
def errored(self):
if (3, 0, 0) <= PY < (3, 4, 0):
return bool(self._outcomeForDoCleanups.errors)
return self.id() in [case.id() for case, _ in self.result.errors]


@property
def failed(self):
if (3, 0, 0) <= PY < (3, 4, 0):
return bool(self._outcomeForDoCleanups.failures)
return self.id() in [case.id() for case, _ in self.result.failures]


@property
def passed(self):
return not (self.errored or self.failed)


def tearDown(self):
if PY >= (3, 4, 0):
self._feedErrorsToResultEarly(self.result, self._outcome.errors)




class TestClass(SmartTestCase):


def test_1(self):
self.assertTrue(True)


def test_2(self):
self.assertFalse(True)


def test_3(self):
self.assertFalse(False)


def test_4(self):
self.assertTrue(False)


def test_5(self):
self.assertHerp('Derp')


def tearDown(self):
super(TestClass, self).tearDown()
log.critical('---- RUNNING {} ... -----'.format(self.id()))
if self.errored:
log.critical('----- ERRORED -----')
elif self.failed:
log.critical('----- FAILED -----')
else:
log.critical('----- PASSED -----')




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

当使用 unittest运行时:

$ ./test.py -v
test_1 (__main__.TestClass) ... ok
test_2 (__main__.TestClass) ... FAIL
test_3 (__main__.TestClass) ... ok
test_4 (__main__.TestClass) ... FAIL
test_5 (__main__.TestClass) ... ERROR
[…]


$ cat ./test.log
CRITICAL __main__: ---- RUNNING __main__.TestClass.test_1 ... -----
CRITICAL __main__: ----- PASSED -----
CRITICAL __main__: ---- RUNNING __main__.TestClass.test_2 ... -----
CRITICAL __main__: ----- FAILED -----
CRITICAL __main__: ---- RUNNING __main__.TestClass.test_3 ... -----
CRITICAL __main__: ----- PASSED -----
CRITICAL __main__: ---- RUNNING __main__.TestClass.test_4 ... -----
CRITICAL __main__: ----- FAILED -----
CRITICAL __main__: ---- RUNNING __main__.TestClass.test_5 ... -----
CRITICAL __main__: ----- ERRORED -----

当使用 nosetests运行时:

$ nosetests ./test.py -v
test_1 (test.TestClass) ... ok
test_2 (test.TestClass) ... FAIL
test_3 (test.TestClass) ... ok
test_4 (test.TestClass) ... FAIL
test_5 (test.TestClass) ... ERROR


$ cat ./test.log
CRITICAL test: ---- RUNNING test.TestClass.test_1 ... -----
CRITICAL test: ----- PASSED -----
CRITICAL test: ---- RUNNING test.TestClass.test_2 ... -----
CRITICAL test: ----- FAILED -----
CRITICAL test: ---- RUNNING test.TestClass.test_3 ... -----
CRITICAL test: ----- PASSED -----
CRITICAL test: ---- RUNNING test.TestClass.test_4 ... -----
CRITICAL test: ----- FAILED -----
CRITICAL test: ---- RUNNING test.TestClass.test_5 ... -----
CRITICAL test: ----- ERRORED -----

背景资料

开始了是这样的:

class SmartTestCase(unittest.TestCase):


"""Knows its state (pass/fail/error) by the time its tearDown is called."""


def run(self, result):
# Store the result on the class so tearDown can behave appropriately
self.result = result.result if hasattr(result, 'result') else result
super(SmartTestCase, self).run(result)


@property
def errored(self):
return self.id() in [case.id() for case, _ in self.result.errors]


@property
def failed(self):
return self.id() in [case.id() for case, _ in self.result.failures]


@property
def passed(self):
return not (self.errored or self.failed)

但是,这只能在 Python2中使用。在 Python 3中,包括3.3在内,控制流似乎发生了一些变化: Python 3的 unittest 包 处理结果 之后调用每个测试的 tearDown()方法... ... 如果我们简单地向测试类添加一行(或六行) ,这种行为就可以得到证实:

@@ -63,6 +63,12 @@
log.critical('----- FAILED -----')
else:
log.critical('----- PASSED -----')
+        log.warning(
+            'ERRORS THUS FAR:\n'
+            + '\n'.join(tc.id() for tc, _ in self.result.errors))
+        log.warning(
+            'FAILURES THUS FAR:\n'
+            + '\n'.join(tc.id() for tc, _ in self.result.failures))




if __name__ == '__main__':

那就再做一次测试:

$ python3.3 ./test.py -v
test_1 (__main__.TestClass) ... ok
test_2 (__main__.TestClass) ... FAIL
test_3 (__main__.TestClass) ... ok
test_4 (__main__.TestClass) ... FAIL
test_5 (__main__.TestClass) ... ERROR
[…]

你会看到你得到的结果是:

CRITICAL __main__: ---- RUNNING __main__.TestClass.test_1 ... -----
CRITICAL __main__: ----- PASSED -----
WARNING  __main__: ERRORS THUS FAR:


WARNING  __main__: FAILURES THUS FAR:


CRITICAL __main__: ---- RUNNING __main__.TestClass.test_2 ... -----
CRITICAL __main__: ----- PASSED -----
WARNING  __main__: ERRORS THUS FAR:


WARNING  __main__: FAILURES THUS FAR:


CRITICAL __main__: ---- RUNNING __main__.TestClass.test_3 ... -----
CRITICAL __main__: ----- PASSED -----
WARNING  __main__: ERRORS THUS FAR:


WARNING  __main__: FAILURES THUS FAR:
__main__.TestClass.test_2
CRITICAL __main__: ---- RUNNING __main__.TestClass.test_4 ... -----
CRITICAL __main__: ----- PASSED -----
WARNING  __main__: ERRORS THUS FAR:


WARNING  __main__: FAILURES THUS FAR:
__main__.TestClass.test_2
CRITICAL __main__: ---- RUNNING __main__.TestClass.test_5 ... -----
CRITICAL __main__: ----- PASSED -----
WARNING  __main__: ERRORS THUS FAR:


WARNING  __main__: FAILURES THUS FAR:
__main__.TestClass.test_2
__main__.TestClass.test_4

现在,将上面的代码与 Python 2的输出进行比较:

CRITICAL __main__: ---- RUNNING __main__.TestClass.test_1 ... -----
CRITICAL __main__: ----- PASSED -----
WARNING  __main__: ERRORS THUS FAR:


WARNING  __main__: FAILURES THUS FAR:


CRITICAL __main__: ---- RUNNING __main__.TestClass.test_2 ... -----
CRITICAL __main__: ----- FAILED -----
WARNING  __main__: ERRORS THUS FAR:


WARNING  __main__: FAILURES THUS FAR:
__main__.TestClass.test_2
CRITICAL __main__: ---- RUNNING __main__.TestClass.test_3 ... -----
CRITICAL __main__: ----- PASSED -----
WARNING  __main__: ERRORS THUS FAR:


WARNING  __main__: FAILURES THUS FAR:
__main__.TestClass.test_2
CRITICAL __main__: ---- RUNNING __main__.TestClass.test_4 ... -----
CRITICAL __main__: ----- FAILED -----
WARNING  __main__: ERRORS THUS FAR:


WARNING  __main__: FAILURES THUS FAR:
__main__.TestClass.test_2
__main__.TestClass.test_4
CRITICAL __main__: ---- RUNNING __main__.TestClass.test_5 ... -----
CRITICAL __main__: ----- ERRORED -----
WARNING  __main__: ERRORS THUS FAR:
__main__.TestClass.test_5
WARNING  __main__: FAILURES THUS FAR:
__main__.TestClass.test_2
__main__.TestClass.test_4

由于 Python 3处理错误/故障 之后,因此测试被撤销,我们不能轻易地在每种情况下使用 result.errorsresult.failures推断测试的结果。(我认为从架构上来说,处理一个测试的结果可能更有意义,之后将其拆除,然而,是的使得根据测试的通过/失败状态遵循不同的测试结束过程的完美有效用例更难满足... ...)

因此,与其依赖于整个 result对象,相反,我们可以引用 _outcomeForDoCleanups,因为 其他人提到了 已经,它包含当前运行的测试的结果对象,并且具有必要的 errorsfailrues属性,我们可以使用这些属性在 tearDown()被调用时推断测试的状态:

@@ -3,6 +3,7 @@
from __future__ import unicode_literals
import logging
import os
+import sys
import unittest




@@ -16,6 +17,9 @@
log = logging.getLogger(__name__)




+PY = tuple(sys.version_info)[:3]
+
+
class SmartTestCase(unittest.TestCase):


"""Knows its state (pass/fail/error) by the time its tearDown is called."""
@@ -27,10 +31,14 @@


@property
def errored(self):
+        if PY >= (3, 0, 0):
+            return bool(self._outcomeForDoCleanups.errors)
return self.id() in [case.id() for case, _ in self.result.errors]


@property
def failed(self):
+        if PY >= (3, 0, 0):
+            return bool(self._outcomeForDoCleanups.failures)
return self.id() in [case.id() for case, _ in self.result.failures]


@property

这增加了对 Python 3早期版本的支持。

然而,在 Python 3.4中,这个私有成员变量 已经不存在了被添加了一个新的(尽管是 还有私有的)方法: _feedErrorsToResult

这意味着对于版本3.4(后来) ,如果需求足够大,就可以ーー 非常粗俗ーー 力量让它像在版本2中那样再次工作... ..。

@@ -27,17 +27,20 @@
def run(self, result):
# Store the result on the class so tearDown can behave appropriately
self.result = result.result if hasattr(result, 'result') else result
+        if PY >= (3, 4, 0):
+            self._feedErrorsToResultEarly = self._feedErrorsToResult
+            self._feedErrorsToResult = lambda *args, **kwargs: None  # no-op
super(SmartTestCase, self).run(result)


@property
def errored(self):
-        if PY >= (3, 0, 0):
+        if (3, 0, 0) <= PY < (3, 4, 0):
return bool(self._outcomeForDoCleanups.errors)
return self.id() in [case.id() for case, _ in self.result.errors]


@property
def failed(self):
-        if PY >= (3, 0, 0):
+        if (3, 0, 0) <= PY < (3, 4, 0):
return bool(self._outcomeForDoCleanups.failures)
return self.id() in [case.id() for case, _ in self.result.failures]


@@ -45,6 +48,10 @@
def passed(self):
return not (self.errored or self.failed)


+    def tearDown(self):
+        if PY >= (3, 4, 0):
+            self._feedErrorsToResultEarly(self.result, self._outcome.errors)
+


class TestClass(SmartTestCase):


@@ -64,6 +71,7 @@
self.assertHerp('Derp')


def tearDown(self):
+        super(TestClass, self).tearDown()
log.critical('---- RUNNING {} ... -----'.format(self.id()))
if self.errored:
log.critical('----- ERRORED -----')

... 提供, 当然,这个类的所有消费者在他们各自的 tearDown方法中记住 super(…, self).tearDown()..。

免责声明: 这纯粹是教育性的,不要在家尝试,等等。我对这个解决方案并不感到特别自豪,但目前看来,它似乎效果不错,而且是我在周六下午拉了一两个小时小提琴后能做出的最好的解决方案... ..。

截至2022年3月,这个答案已经更新为支持 Python 版本介于3.4和3.11之间(包括最新的 Python 开发版本)。错误/故障的分类与输出 unittest中使用的相同。它在 tearDown()之前不需要修改任何代码就可以工作。它能正确识别装饰器 skipIf()expectedFailure。它也与 派特兼容。

密码:

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。它只能识别预期失败块中的意外成功

测试方法示例:

    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)

完整的代码 ,包括  龟裂装饰器例子

编辑: 当我将这个解决方案更新到 Python 3.11时,我删除了与旧的 Python < 3.4相关的所有内容以及许多次要注释。

对于那些对使用依赖于 unittest内部结构的解决方案感到不舒服的人,这里有一个解决方案:

首先,我们创建一个装饰器,它将在 TestCase实例上设置一个标志,以确定测试用例是否失败或通过:

import unittest
import functools


def _tag_error(func):
"""Decorates a unittest test function to add failure information to the TestCase."""


@functools.wraps(func)
def decorator(self, *args, **kwargs):
"""Add failure information to `self` when `func` raises an exception."""
self.test_failed = False
try:
func(self, *args, **kwargs)
except unittest.SkipTest:
raise
except Exception:  # pylint: disable=broad-except
self.test_failed = True
raise  # re-raise the error with the original traceback.


return decorator

这个装潢师其实很简单。它依赖于这样一个事实,即 unittest通过 例外检测失败的测试。据我所知,唯一需要处理的 特别的异常是 unittest.SkipTest(它并不表示测试失败)。所有其他异常都表示测试失败,因此当它们冒泡出现时,我们将它们标记为失败。

我们现在可以直接使用这个装饰器:

class MyTest(unittest.TestCase):
test_failed = False


def tearDown(self):
super(MyTest, self).tearDown()
print(self.test_failed)


@_tag_error
def test_something(self):
self.fail('Bummer')

一直给这个装修师写信会很烦人的。有什么办法可以简化吗?有的! *我们可以编写一个元类来处理如何为我们应用装饰器:

class _TestFailedMeta(type):
"""Metaclass to decorate test methods to append error information to the TestCase instance."""
def __new__(cls, name, bases, dct):
for name, prop in dct.items():
# assume that TestLoader.testMethodPrefix hasn't been messed with -- otherwise, we're hosed.
if name.startswith('test') and callable(prop):
dct[name] = _tag_error(prop)


return super(_TestFailedMeta, cls).__new__(cls, name, bases, dct)

现在我们把这个应用到我们的基 TestCase子类中,我们就完成了:

import six  # For python2.x/3.x compatibility


class BaseTestCase(six.with_metaclass(_TestFailedMeta, unittest.TestCase)):
"""Base class for all our other tests.


We don't really need this, but it demonstrates that the
metaclass gets applied to all subclasses too.
"""




class MyTest(BaseTestCase):


def tearDown(self):
super(MyTest, self).tearDown()
print(self.test_failed)


def test_something(self):
self.fail('Bummer')

可能有许多情况下,这不能正确处理。例如,它不能正确地检测到失败的子测试 < a href = “ https://docs.python.org/3/library/unittest.html # different-test-iterations-using-subtest”rel = “ nofollow noReferrer”> subtest 或预期的故障。我对其他的失败模式很感兴趣,所以如果你发现我没有正确处理一个案例,请在评论中告诉我,我会研究它。


如果没有更简单的方法,我就不会把 _tag_error变成一个私有函数; -)

针对 Python 3.7进行了测试——获取失败断言信息的示例代码,但是可以提供如何处理错误的想法:

def tearDown(self):
if self._outcome.errors[1][1] and hasattr(self._outcome.errors[1][1][1], 'actual'):
print(self._testMethodName)
print(self._outcome.errors[1][1][1].actual)
print(self._outcome.errors[1][1][1].expected)

简而言之,如果所有测试运行到目前为止都没有错误或失败,那么这就给出了 True:

class WatheverTestCase(TestCase):


def tear_down(self):
return not self._outcome.result.errors and not self._outcome.result.failures

探索 _outcome的属性以获得更详细的可能性。

我认为对你的问题的正确答案是,在 tearDown()中没有一种 干净的方法来获得测试结果。这里的大多数答案都涉及到访问 Pythonunittest模块的一些私有部分,通常感觉像是解决方案。我强烈建议避免这些,因为测试结果和测试用例是解耦的,您不应该与之对抗。

如果你喜欢干净的代码(像我一样) ,我认为你应该做的是实例化你的 测试跑者与自己的 测试结果类。然后您可以通过覆盖这些方法添加任何您想要的报告:

addError(test, err)
Called when the test case test raises an unexpected exception. err is a tuple of the form returned by sys.exc_info(): (type, value, traceback).


The default implementation appends a tuple (test, formatted_err) to the instance’s errors attribute, where formatted_err is a formatted traceback derived from err.


addFailure(test, err)
Called when the test case test signals a failure. err is a tuple of the form returned by sys.exc_info(): (type, value, traceback).


The default implementation appends a tuple (test, formatted_err) to the instance’s failures attribute, where formatted_err is a formatted traceback derived from err.


addSuccess(test)
Called when the test case test succeeds.


The default implementation does nothing.

这很简单,只使用公共 API,并且可以在任何 python 版本上工作:

import unittest


class MyTest(unittest.TestCase):
def defaultTestResult():
self.lastResult = unittest.result.TestResult()
return self.lastResult


...


使用全局变量的 Python 版本独立代码

import unittest
global test_case_id
global test_title
global test_result
test_case_id =''
test_title = ''
test_result = ''
class Dummy(unittest.TestCase):
def setUp(self):
pass
def tearDown(self):
global test_case_id
global test_title
global test_result
self.test_case_id = test_case_id
self.test_title = test_title
self.test_result = test_result
print('Test case id is : ',self.test_case_id)
print('test title is : ',self.test_title)
print('Test test result is : ',self.test_result)
def test_a(self):
global test_case_id
global test_title
global test_result
test_case_id = 'test1'
test_title = 'To verify test1'
test_result=self.assertTrue(True)
def test_b(self):
global test_case_id
global test_title
global test_result
test_case_id = 'test2'
test_title = 'To verify test2'
test_result=self.assertFalse(False)
if __name__ == "__main__":
unittest.main()