python 单元测试_python 单元测试中处理用例失败的情况

77a3b45ae202da8fbc546550bae2b8a1.png
今天有一个需求, 在 单元测试失败的时候打印一些日志, 我们管他叫 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的时候做一些操作

原文作者:ywhyme

原出处:网络

转载声明:以上内容来源于网络,版权归原作者所有,如来源信息有误或侵犯权益,请联系我们删除或授权事宜。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值