IT数码 购物 网址 头条 软件 日历 阅读 图书馆
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
图片批量下载器
↓批量下载图片,美女图库↓
图片自动播放器
↓图片自动播放器↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁
 
   -> 开发测试 -> unittest 如果用例失败,跳过所有测试用例 -> 正文阅读

[开发测试]unittest 如果用例失败,跳过所有测试用例

1. 知识点

  1. 使用装饰器。
  2. 熟悉一遍unittest.TestCase源码(这里主要用到_Outcome类)。

2. TesetCase

官方介绍:

TestCase 类的实例代表 unittest Universe 中的逻辑测试单元。此类旨在用作基类,具体测试由具体子类实现。此类实现了测试运行程序所需的接口以允许其驱动测试,以及测试代码可用于检查和报告各种故障的方法。

通俗说就是:TestCase 类是一个基类,测试类需要继承unittest.TestCase 类,测试类来实现相关测试用例。unittest.TestCase 类实现了一些接口(Hook method),例如setUptearDownsetUpClasstearDownClass,以及检查和报告各种故障的方法。

3. run()方法

run()方法是执行测试用例的地方,也是TesetCase类的核心方法。

源码:

 def run(self, result=None):
        if result is None:
            result = self.defaultTestResult()
            startTestRun = getattr(result, 'startTestRun', None)
            stopTestRun = getattr(result, 'stopTestRun', None)
            if startTestRun is not None:
                startTestRun()
        else:
            stopTestRun = None

        result.startTest(self)
        try:
            testMethod = getattr(self, self._testMethodName)
            if (getattr(self.__class__, "__unittest_skip__", False) or
                getattr(testMethod, "__unittest_skip__", False)):
                # If the class or method was skipped.
                skip_why = (getattr(self.__class__, '__unittest_skip_why__', '')
                            or getattr(testMethod, '__unittest_skip_why__', ''))
                self._addSkip(result, self, skip_why)
                return result

            expecting_failure = (
                getattr(self, "__unittest_expecting_failure__", False) or
                getattr(testMethod, "__unittest_expecting_failure__", False)
            )
            outcome = _Outcome(result)
            try:
                self._outcome = outcome

                with outcome.testPartExecutor(self):
                    self._callSetUp()
                if outcome.success:
                    outcome.expecting_failure = expecting_failure
                    with outcome.testPartExecutor(self, isTest=True):
                        self._callTestMethod(testMethod)
                    outcome.expecting_failure = False
                    with outcome.testPartExecutor(self):
                        self._callTearDown()

                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:
                # 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

        finally:
            result.stopTest(self)
            if stopTestRun is not None:
                stopTestRun()

分为几个步骤来看:

第一部分:
(调用self.defaultTestResult(),创建了一result,用于记录测试结果。)

  def run(self, result=None):
        if result is None:
            result = self.defaultTestResult()
            startTestRun = getattr(result, 'startTestRun', None)
            stopTestRun = getattr(result, 'stopTestRun', None)
            if startTestRun is not None:
                startTestRun()
        else:
            stopTestRun = None

        result.startTest(self)

首先判断是否传入了result,如果用unittest.main()执行的测试用例,默认result=None。使用self.defaultTestResult() 方法创建了一个TestResult类。然后查找TestResult类中有没有的startTestRunstopTestRun方法,如果有这个方法则调用,但是startTestRunstopTestRun默认没有实现任何功能,需要用户自定义。最后一句是执行startTest方法。主要初始化一些用例统计数字、缓存等。

第二部分:
(找是否有需要跳过和预期失败的测试用例)。

 try:
            testMethod = getattr(self, self._testMethodName)
            if (getattr(self.__class__, "__unittest_skip__", False) or
                getattr(testMethod, "__unittest_skip__", False)):
                # If the class or method was skipped.
                skip_why = (getattr(self.__class__, '__unittest_skip_why__', '')
                            or getattr(testMethod, '__unittest_skip_why__', ''))
                self._addSkip(result, self, skip_why)
                return result
                
             expecting_failure = (
                getattr(self, "__unittest_expecting_failure__", False) or
                getattr(testMethod, "__unittest_expecting_failure__", False)
            )

获取用例的名称,判断TestCase类或者用例是否有标记跳过的unittese_skip,通过self._addSkip() 方法向result中添加skip的原因,然后 return result,expecting_failure:获取测试类或用例中带有标记预期失败的用例。

第三部分:
开始执行测试用例。创建_Outcome类的实例对象,传入result,并赋值给TestCase类的self._outcome属性,用户记录信息。

outcome = _Outcome(result)
try:
     self._outcome = outcome

     with outcome.testPartExecutor(self):
            self._callSetUp()  // 执行用例的setUp() 方法
     if outcome.success:
          outcome.expecting_failure = expecting_failure  // 记录将预期失败的用例
          with outcome.testPartExecutor(self, isTest=True):
               self._callTestMethod(testMethod)  // 执行用例
          outcome.expecting_failure = False   
          with outcome.testPartExecutor(self):
               self._callTearDown()  // 执行用例的tearDown()方法
	 self.doCleanups()
  1. outcome = _Outcome(result):存储TestResult的执行结果。
  2. 然后开启testPartExecutor 上下文处理器开始执行用例。
  3. self._callSetUp() :执行用例中的setUp()方法。
  4. self._callTestMethod(testMethod) ::执行用例。
  5. self._callTearDown():执行用例的tearDown()方法。

第四部分:
执行完用例之后,处理成功、跳过、失败、预期失败的用例添加到result中。

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

for循环将outcome是否有跳过的用例,如果有则添加到result中。if 用来判断第三部分的执行测试用例或者tearDown是否成功,如果运行没有报错或者出错,将预期失败的用例和成功的用例结果都添加到result中。

_feedErrorsToResult处理错误消息的方法。
将错误失败的用例信息添加到result中。

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)

最后一步:
做清除操作。

   finally:
                # 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

        finally:
            result.stopTest(self)
            if stopTestRun is not None:
                stopTestRun()

3. 注意

每一个case都是TestCase类的一个实例化对象(重要)。
每一个case都是TestCase类的一个实例化对象(重要)。
每一个case都是TestCase类的一个实例化对象(重要)。

例如:
用例test_1unittest.TestCase类的一个实例对象。
用例test_2也是unittest.TestCase类的一个实例对象。

test_1实例对象 != test_2实例对象

class TestDemo(unittest.TestCase):

    def test_1(self):
      self.a = 1
      print('第一个测试用例')

    def test_2(self):
      print('第二个测试用例')
      // print(self.a)  不能访问a


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

4. 解决方案

通过上面简单的分析可以知道,_Outcome类记录测试用例成功、失败、跳过的信息,并且通过TeseCase类中的self._outcome 接收。并且源码中也是通过传入outcome.errors 处理失败的用例。

_Outcome类部分源码
outcome = _Outcome(result),这个地方已经传过值了。

outcome = _Outcome(result) 

class _Outcome(object):
    def __init__(self, result=None):
        self.expecting_failure = False
        self.result = result
        self.result_supports_subtests = hasattr(result, "addSubTest")
        self.success = True
        self.skipped = []
        self.expectedFailure = None
        self.errors = []

在这里插入图片描述
所以可以使用self._outcome.result.failures 来获取失败的用例。

if self._outcome.result.failures or self._outcome.result.errors:
	// self._outcome.result.failures[0][0]._testMethodName 用例名称
    raise unittest.SkipTest(func.__name__,self._outcome.result.failures[0][0]._testMethodName)  

5. 最终代码

使用装饰器

import unittest
from functools import wraps


def CustomSkip(func):
  @wraps(func)
  def wrappend(self):
    if self._outcome.result.failures or self._outcome.result.errors:
      raise unittest.SkipTest(func.__name__,self._outcome.result.failures[0][0]._testMethodName)
    func(self)

  return wrappend


class TestDemo(unittest.TestCase):

    def test_1(self):
      print('第一个测试用例')


    @CustomSkip
    def test_a(self):
     
        self.assertEqual(1,3)

    @CustomSkip
    def test_b(self):
        print('测试用例bbbbb')

    @CustomSkip
    def test_c(self):
        print('测试用例ccccc')


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



# 第一个测试用例
# .Fss
# ======================================================================
# FAIL: test_a (__main__.TestDemo)
# ----------------------------------------------------------------------
# Traceback (most recent call last):
#   File "d:\project\fist_unittest\test.py", line 10, in wrappend
#     func(self)
#   File "d:\project\fist_unittest\test.py", line 24, in test_a
#     self.assertEqual(1,3)
# AssertionError: 1 != 3

# ----------------------------------------------------------------------
# Ran 4 tests in 0.010s

# FAILED (failures=1, skipped=2)

其他学习链接:

可以参考大神的:
1.深入解读Python的unittest并拓展HTMLTestRunner
2.unittest跳过测试用例进阶版-----上一个测试用例失败后跳过下一个测试用例

有不对的地方请大家帮忙看看,一起学习学习!

  开发测试 最新文章
pytest系列——allure之生成测试报告(Wind
某大厂软件测试岗一面笔试题+二面问答题面试
iperf 学习笔记
关于Python中使用selenium八大定位方法
【软件测试】为什么提升不了?8年测试总结再
软件测试复习
PHP笔记-Smarty模板引擎的使用
C++Test使用入门
【Java】单元测试
Net core 3.x 获取客户端地址
上一篇文章      下一篇文章      查看所有文章
加:2022-01-28 12:13:05  更:2022-01-28 12:13:48 
 
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁

360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年11日历 -2024/11/18 4:27:38-

图片自动播放器
↓图片自动播放器↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  IT数码