Joe*_*ert 62 python unit-testing nose
是否有可能在tearDown()方法中获得测试结果(即是否所有断言都已通过)?我正在运行Selenium脚本,我想从tearDown()内部做一些报告,但我不知道这是否可行.
sco*_*fey 39
如果查看实现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()
Run Code Online (Sandbox Code Playgroud)
编辑:这适用于Python 2.6 - 3.3,(修改为新的Python 下面).
hyn*_*cer 36
此解决方案适用于Python版本2.7到3.7(当前最高版本),之前没有任何装饰器或任何代码中的其他修改tearDown.一切都按照内置的结果分类进行.也跳过测试或expectedFailure正确识别.它评估当前测试的结果,而不是迄今为止所有测试的摘要.兼容pytest.
import unittest
class MyTest(unittest.TestCase):
def tearDown(self):
if hasattr(self, '_outcome'): # Python 3.4+
result = self.defaultTestResult() # these 2 methods have no side effects
self._feedErrorsToResult(result, self._outcome.errors)
else: # Python 3.2 - 3.3 or 3.0 - 3.1 and 2.7
result = getattr(self, '_outcomeForDoCleanups', self._resultForDoCleanups)
error = self.list2reason(result.errors)
failure = self.list2reason(result.failures)
ok = not error and not failure
# demo: report short info immediately (not important)
if not ok:
typ, text = ('ERROR', error) if error else ('FAIL', failure)
msg = [x for x in text.split('\n')[1:] if not x.startswith(' ')][0]
print("\n%s: %s\n %s" % (typ, self.id(), msg))
def list2reason(self, exc_list):
if exc_list and exc_list[-1][0] is self:
return exc_list[-1][1]
# DEMO tests
def test_success(self):
self.assertEqual(1, 1)
def test_fail(self):
self.assertEqual(2, 1)
def test_error(self):
self.assertEqual(1 / 0, 1)
Run Code Online (Sandbox Code Playgroud)
注释:只需报告一个或零个异常(错误或失败),因为之前不能再预期tearDown.该程序包unittest期望tearDown可以引发第二个异常.因此,列表errors和failures可以包含拆卸之前一起只有一个或零个元素."演示"评论后的行报告了一个简短的结果.
演示输出:(不重要)
$ python3.5 -m unittest test
EF.
ERROR: test.MyTest.test_error
ZeroDivisionError: division by zero
FAIL: test.MyTest.test_fail
AssertionError: 2 != 1
==========================================================
... skipped usual output from unittest with tracebacks ...
...
Ran 3 tests in 0.002s
FAILED (failures=1, errors=1)
Run Code Online (Sandbox Code Playgroud)
与其他解决方案的比较 - (关于Python源代码库的提交历史):
此解决方案像许多其他解决方案一样使用TestCase实例的私有属性,但我仔细检查了Python源代码库中的所有相关提交,其中三个替代名称涵盖了自Python 2.7到3.6.2之后的代码历史记录,没有任何差距.在一些新的主要Python发布之后它可能是一个问题,但它可以被清楚地识别,跳过并在以后轻松修复为新的Python.一个优点是在运行tearDown之前没有任何内容被修改,它应该永远不会破坏测试并且支持unittest的所有功能,与pytest一起工作并且它可以工作许多扩展包,但不能与nosetest一起使用(不是因为鼻子测试不兼容所以不兼容,例如与unittest.expectedFailure).
在用户测试方法上使用装饰器或使用自定义 failureException(mgilson,Pavel Repin 2nd way,kenorb)的解决方案对于未来的Python版本是强大的,但是如果一切都应该完全有效,它们会像雪球一样增长,具有更多支持的异常和更多复制的单元测试内部.装饰函数具有不太可读的回溯(甚至更多级别由一个装饰器添加),它们对于调试来说更复杂,并且如果另一个更重要的装饰器有问题则是不愉快的.(感谢mgilson,基本功能已准备就绪,可以修复已知问题.)
具有修改run方法和捕获result
参数的解决方案
result拆机电话后更新,前所未有.解决方案exc_info()(Pavel Repin第2版)仅适用于Python 2.
其他解决方案主要相似,但不完整或缺点较多.
由Python source repository
= Lib/unittest/case.py =
Python v 2.7 - 3.3解释
class TestCase(object):
...
def run(self, result=None):
...
self._outcomeForDoCleanups = result # Python 3.2, 3.3
# self._resultForDoCleanups = result # Python 2.7
# # Python 2.6 - no result saved
...
try:
testMethod()
except... # many times for different exception classes
result.add...(self, sys.exc_info()) # _addSkip, addError, addFailure
...
try:
self.tearDown()
...
Run Code Online (Sandbox Code Playgroud)
Python v.3.4 - 3.6
def run(self, result=None):
...
# outocome is a context manager to catch and collect different exceptions
self._outcome = outcome
...
with outcome...(self):
testMethod()
...
with outcome...(self):
self.tearDown()
...
self._feedErrorsToResult(result, outcome.errors)
Run Code Online (Sandbox Code Playgroud)
注意(通过阅读Python提交消息):测试结果与测试分离的原因是内存泄漏预防.每个异常信息都可以访问失败的进程状态的帧,包括所有局部变量.如果将帧分配给代码块中也可能失败的局部变量,则可以轻松创建跨内存refence.由于垃圾收集器的缘故,它并不可怕,但是如果内存可以正确释放,则可用内存可以更快地碎片化.这就是为什么异常信息和回溯很快转换为字符串以及为什么临时对象self._outcome被封装并在finally块中设置为None以防止内存泄漏的原因.
Pav*_*pin 14
CAVEAT:目前我没办法仔细检查以下理论,远离开发盒.所以这可能是在黑暗中拍摄的.
也许您可以检查sys.exc_info()tearDown()方法内部的返回值,如果它返回(None, None, None),您就知道测试用例成功了.否则,您可以使用返回的元组来查询异常对象.
请参阅sys.exc_info文档.
另一种更明确的方法是编写一个方法装饰器,您可以将其打到所有需要这种特殊处理的测试用例方法上.这个装饰器可以拦截断言异常,并根据它修改一些状态,self允许你的tearDown方法学习什么.
@assertion_tracker
def test_foo(self):
# some test logic
Run Code Online (Sandbox Code Playgroud)
ama*_*nes 10
如果您使用的是Python2,则可以使用该方法_resultForDoCleanups.此方法返回一个TextTestResult对象:
<unittest.runner.TextTestResult run=1 errors=0 failures=0>
您可以使用此对象检查测试结果:
def tearDown(self):
if self._resultForDoCleanups.failures:
...
elif self._resultForDoCleanups.errors:
...
else:
#Success
Run Code Online (Sandbox Code Playgroud)
如果您使用的是Python3,则可以使用_outcomeForDoCleanups:
def tearDown(self):
if not self._outcomeForDoCleanups.success:
...
Run Code Online (Sandbox Code Playgroud)
继amatellanes的回答之后,如果您使用的是Python3.4,则无法使用_outcomeForDoCleanups.这是我设法破解的内容:
def _test_has_failed(self):
for method, error in self._outcome.errors:
if error:
return True
return False
Run Code Online (Sandbox Code Playgroud)
令人讨厌,但它似乎工作.
这取决于您希望生成哪种报告.
如果您想要在失败时执行某些操作(例如生成屏幕截图),而不是使用tearDown(),则可以通过覆盖来实现failureException.
例如:
@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
Run Code Online (Sandbox Code Playgroud)