Unittest二次开发实战

本文介绍如何对Python的unittest框架进行二次开发,重点在于自定义TestResult类,以实现测试结果的详细记录,包括用例的开始时间、持续时间、tags、level等信息,并提供用例输出捕获、异常处理等功能,旨在提高测试报告的可读性和实用性。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

目录

  前言

  unittest.TestResult类简介

  TestResult类定制目标

  实现步骤

  测试结果summary格式规划

  单个用例结果格式规划

  用例tags和level的实现

  根据测试方法对象获取用例代码

  单个用例结果类的实现

  TestResult属性及初始化方法

  测试开始和测试结束

  用例开始和用例结束

  1. 重写恢复输出流方法

  2. 用例开始和结束方法

  用例结果注册

  测试本TestResult类方法

  其他函数和方法

  1. 用例状态列

  2. 获取平台信息

  3. 从异常中提取异常信息方法

  4. 从异常和已知异常中提取失败原因的方法


  前言

  Unittest是Python自带的自动化测试框架,提供了基本的控制结构和模型概念。

  由于Unittest功能较为基础,因此在实际框架实战中往往需要对其功能进行扩充。

  比如:

  ·生成HTML报告

  ·多线程并发(并且报告不混乱)

  ·自动重试出错用例

  ·为用例提供tags标签和level等级等,往往需要我们对Unittest框架进行二次开发和扩展,由于Unittest框架清晰的API,扩展和定制也非常方便。

  unittest.TestResult类简介

  TestResult类一般在TestRunner类中实例化,并穿梭于每个执行的测试套件和测试用例中用于记录结果。

  TestResult对象常用的属性有:

  ·stream:用于输出测试信息的IO流,一般是终端或文本文件。

  ·descriptions:描述信息。

  ·verbosity:显示详细级别。

  ·buffer:默认为False,用例中的print信息立即输出,buffer为True时将用例中的print信息统一收集并集中输出。

  ·tb_locals: 在报错异常信息中显示用例中的局部变量(即tackback_locals)。

  ·failfast:默认为False, 用例失败后继续运行,为True时,任何一条用例失败时立即停止。

  ·_mirrorOutput:是否重定向输出流状态标志unittest.TestResult类提供了以下几种方法:

      -运行开始/结束

        startTestRun: 执行开始时调用,参考unittest.TextTestRunner中的run方法。

        stopTestRun: 所有用例执行结束后调用

        startTest:单个用例执行开始时调用,参考unittest.TestCase类中的run方法。

        stopTest:单个用例执行结束后调用。

      -注册用例结果

        addSuccess:单个用例执行成功时调用,来注册结果,默认为空。

        addFailure:用例失败时在stopTest前调用。

        addError:用例异常时在stopTest前调用。

        addSkip:用例跳过时在stopTest前调用。

        addExpectedFailure:用例期望失败时在stopTest前调用。

        addUnexpectedSuccess:用例非期望成功时在stopTest前调用。

      -重定向和恢复系统输出流

        _setupStdout:重定向输出流,默认self.buffer为True时生效

        _restoreStdout:恢复系统输出流

  用例失败Failure和用例异常Error的区别:

  用例中的断言错误(期望结果和实际结果不一致)引发的AssertionError异常被视为用例失败,其他异常视为用例异常Error。

  ExpectedFailure和UnexpectedSuccess: 期望失败指我们期望这条用例执行失败,即用例失败了才是符合预期的,而没有失败即UnexpectedSuccess,这是一种反向用例,如果失败了其实是通过,而成功了反而是失败。

  TestResult类定制目标

  1. 在result中增加整体的运行开始时间start_at,持续时间duration和每条用例的开始时间,执行时间

  2. 存储用例中的print信息及异常信息,以供生成HTML使用

  3. 为已知异常提供失败原因

  4. 提供结构化和可序列化的summary和详情数据

  5. 探测每个用例code,以为审视用例代码提供方便

  6. 增加运行平台platform信息和运行时的环境变量信息

  7. 将print信息改为使用log记录,增加日志时间,方便追溯。

  8. 提供用例的更多的信息,如tags,level, id, 描述等信息。

  实现步骤

  测试结果summary格式规划

  测试结果result类提供一个summary属性,格式如下(参考了httprunner的summary格式):

name: result结果名称
  success: 整个测试结果是否成功
  stat: # 结果统计信息
    testsRun: 总运行数
    successes: 成功数
    failures: 失败数
    errors: 异常数
    skipped: 跳过的用例数
    expectedFailures: 期望失败数
    unexpectedSuccesses: 非期望成功数
  time:
    start_at: 整个测试开始时间(时间戳)
    end_at: 增高测试结束时间(时间戳)
    duration: 整个测试执行耗时(秒)
  platform:
    platform: 执行平台信息
    system: 执行操作系统信息
    python_version: Python版本信息
    # env: 环境变量信息(信息中可能包含账号等敏感信息)
  details:  # 用例结果详情
    - ... # 单个用例结果

  单个用例结果格式规划

# 执行前可获取的信息
  name: 用例名称或用例方法名
  id: 用例完整路径(模块-类-用例方法名)
  decritpion: 用例描述(用例方法docstring第一行)
  doc: 用例方法完整docstring
  module_name: 用例模块名
  class_name: 用例类名
  class_id: 用例类路径(模块-类)
  class_doc: 用例类docstring描述
  tags: 用例标签
  level: 用例等级
  code: 用例代码
  # 执行后可获取的信息
  time:
    start_at: 用例执行开始时间
    end_at: 用例结束时间
    duration: 用例执行持续时间
  status: 用例执行状态success/fail/error/skipped/xfail/xpass
  output: 用例中的print输出信息
  exc_info: 用例异常追溯信息
  reason: 用例跳过,失败,出错的原因

  读者也可以根据自己的需要添加其他额外的信息,如timeout用例超时时间配置,order用例执行顺序,images用例中的截图,link用例中的链接等信息。

  以上的tags和level通过在用例方法的docstring中注入"tag:smoke"及"level:1"等样式为用例添加标签和等级,然后配合定制的loader用例加载器去收集指定标签或等级的用例,下节会详细讲解。

  用例tags和level的实现

  每个框架都会有自己约定格式,这里我采用在docstring注入特定格式描述的方式为用例添加tags和level信息,用例格式如下。

import unittest
  class TestDemo(unittest.TestCase):
      def test_a(self):
          """测试a
          tag:smoke
          tag:demo
          level:1
          """
          print('测试a')

  对于每个用例对象,可以使用test._testMethodDoc来获取其完整的docstring字符串,然后通过正则匹配来匹配出用例的tags列表和level等级,实现方法如下。

import re
  TAG_PARTTEN = 'tag:(\w+)'
  LEVEL_PARTTEN = 'level:(\d+)'
  def get_case_tags(case: unittest.TestCase) -> list:
      """从用例方法的docstring中匹配出指定格式的tags"""
      case_tags = None
      case_doc = case._testMethodDoc
      if case_doc and 'tag' in case_doc:
          pattern = re.compile(TAG_PARTTEN)
          case_tags = re.findall(pattern, case_doc)
      return case_tags
  def get_case_level(case: unittest.TestCase
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值