今天有一个需求, 在单元测试失败的时候打印一些日志, 我们管他叫 dosomething 吧 ,反正就是做一些操作

查了下并没有查到相关的方法, 于是研究了一波unittest 的源码

发现了这个东西

try:
self._outcome = outcome with outcome.testPartExecutor(self):
self.setUp()
if outcome.success:
outcome.expecting_failure = expecting_failure
with outcome.testPartExecutor(self, isTest=True):
testMethod()
outcome.expecting_failure = False
with outcome.testPartExecutor(self):
self.tearDown() self.doCleanups()
for test, reason in outcome.skipped:
self._addSkip(result, test, reason)
self._feedErrorsToResult(result, outcome.errors)
if outcome.success:
if expecting_failure:
if outcome.expectedFailure:
self._addExpectedFailure(result, outcome.expectedFailure)
else:
self._addUnexpectedSuccess(result)
else:
result.addSuccess(self)
return result
finally:
result.stopTest(self)
if orig_result is None:
stopTestRun = getattr(result, 'stopTestRun', None)
if stopTestRun is not None:
stopTestRun() # explicitly break reference cycles:
# outcome.errors -> frame -> outcome -> outcome.errors
# outcome.expectedFailure -> frame -> outcome -> outcome.expectedFailure
outcome.errors.clear()
outcome.expectedFailure = None # clear the outcome, no more needed
self._outcome = None

其中重点关注下testMethod() 这个正是我们执行的用例

于是去看了下testPartExecutor 用例失败的处理是在这里进行处理的

def testPartExecutor(self, test_case, isTest=False):
old_success = self.success
self.success = True
try:
yield
except KeyboardInterrupt:
raise
except SkipTest as e:
self.success = False
self.skipped.append((test_case, str(e)))
except _ShouldStop:
pass
except:
exc_info = sys.exc_info()
if self.expecting_failure:
self.expectedFailure = exc_info
else:
self.success = False
self.errors.append((test_case, exc_info))
# explicitly break a reference cycle:
# exc_info -> frame -> exc_info
exc_info = None
else:
if self.result_supports_subtests and self.success:
self.errors.append((test_case, None))
finally:
self.success = self.success and old_success

奈何, 他只是在self.errors (其中self为我们测试类的一个实例)中加了点东西

于是对self.errors 进行观察,发现及时用例是正常的,他依然由内容.

这..............于是我想到他最终是怎么打出来失败的log的

看到 上面代码中的 self._feedErrorsToResult(result, outcome.errors)

于是找到了这个东西

def _feedErrorsToResult(self, result, errors):
for test, exc_info in errors:
if isinstance(test, _SubTest):
result.addSubTest(test.test_case, test, exc_info)
elif exc_info is not None:
if issubclass(exc_info[0], self.failureException):
result.addFailure(test, exc_info)
else:
result.addError(test, exc_info)

从上面的代码我们可以知道 如果 error的第二项是None那么就是一个执行成功的用例,经过实验并确认了这个事情

现在知道了 unittest 是如何处理 失败用例的了

于是便有了下面这种方法

def tearDown(self):
errors = self._outcome.errors
for test, exc_info in errors:
if exc_info:
# dosomething
pass

上面这种方法尽量少的改变原来的逻辑, 想到一种新的方法解决问题

既然unittest没有处理这个事情,那我们魔改之

于是有了下面这种方法

注意: 不建议魔改代码

import sys
import contextlib
import unittest
from unittest.case import SkipTest, _ShouldStop, _Outcome @contextlib.contextmanager
def testPartExecutor(self, test_case, isTest=False):
old_success = self.success
self.success = True
try:
yield
except Exception:
try:
# if error
getattr(test_case, test_case._testMethodName).__func__._error = True
raise
except KeyboardInterrupt:
raise
except SkipTest as e:
self.success = False
self.skipped.append((test_case, str(e)))
except _ShouldStop:
pass
except:
exc_info = sys.exc_info()
if self.expecting_failure:
self.expectedFailure = exc_info
else:
self.success = False
self.errors.append((test_case, exc_info))
# explicitly break a reference cycle:
# exc_info -> frame -> exc_info
exc_info = None
else:
if self.result_supports_subtests and self.success:
self.errors.append((test_case, None))
finally:
self.success = self.success and old_success _Outcome.testPartExecutor = testPartExecutor class MyTest(unittest.TestCase):
def test_1(self):
print("test_1") def test_2(self):
print("test_2")
raise ValueError def tearDown(self):
if hasattr(getattr(self, self._testMethodName), "_error"):
# dosomething
pass # def tearDown(self):
# 推荐这种方法
# errors = self._outcome.errors
# for test, exc_info in errors:
# if exc_info:
# # dosomething
# pass if __name__ == '__main__':
unittest.main()

这样我们就可以在用例执行失败后在tearDown的时候做一些操作

最新文章

  1. [转]win 10 开始菜单(Win 7风格)增强工具 StartIsBack++ v1.3.4 简体中文特别版
  2. 使用PHPExcel导出文件
  3. C#中的线程(一)入门
  4. R语言数据的输入
  5. GENERATED_UCLASS_BODY 和 GENERATED_BODY 区别
  6. centos下安装nginx和php-fpm
  7. .net闭包的应用
  8. 统计难题 HDOJ --1251
  9. (转) linux虚拟机中和主机三种网络连接方式的区别
  10. Java DES 测试
  11. 【翻译】ASP.NET Web API是什么?
  12. Spark集群搭建步骤
  13. 使用JSR-303进行校验 @Valid
  14. fsync和fdatasync
  15. 了解sso原理
  16. 【【洛谷P2678 跳石头】——%%%ShawnZhou大佬】
  17. 56.两数之和.md
  18. Alienware 15 R3 装Ubuntu 和 win10 双系统
  19. 用R理解统计学
  20. c#之hello world

热门文章

  1. JS执行环境栈及事件循环机制—简洁明了的讲解
  2. CloudEngine 6800基础配置-01_常见密码操作
  3. dbms_metadata.get_ddl的用法
  4. cocos2dx 编译时间长问题
  5. [译]使用AssetBundle Manader
  6. Android:解决Gradle DSL method not found: 'runProguard()' 问题
  7. Bing Maps进阶系列一:初识Bing Maps地图服务
  8. 将本地文件复制到hadoop文件系统
  9. Graphics.DrawMeshInstanced
  10. Testng1