python 自动化福音,30行代码手撸ddt模块
用 python 做过自动化的小伙伴,大多数都应该使用过 ddt 这个模块,不可否认 ddt 这个模块确实挺好用,可以自动根据用例数据,来生成测试用例,能够很方便的将测试数据和测试用例执行的逻辑进行分离。
接下来就带大家一起手把手撸出一个 ddt:
1、DDT 的实现原理
首先我们来看一下 ddt 的基本使用:
ddt 在使用时非常简洁,也就是两个装饰器,@ddt 这个装饰器装饰测试类,@data 这个装饰器装饰器用例方法并传入测试数据。这两个装饰器实现的效果就是根据传入的用例数据自动生成用例。
如果你想学习自动化测试,我这边给你推荐一套视频,这个视频可以说是B站播放全网第一的自动化测试教程,同时在线人数到达1000人,并且还有笔记可以领取及各路大神技术交流:798478386
【已更新】B站讲的最详细的Python接口自动化测试实战教程全集(实战最新版)_哔哩哔哩_bilibili【已更新】B站讲的最详细的Python接口自动化测试实战教程全集(实战最新版)共计200条视频,包括:1、接口自动化之为什么要做接口自动化、2、接口自动化之request全局观、3、接口自动化之接口实战等,UP主更多精彩视频,请关注UP账号。https://www.bilibili.com/video/BV17p4y1B77x/?spm_id_from=333.337.search-card.all.click
具体是怎么实现的呢?其实实现的思路也特别的简单,也就两个步骤:
第一步:把传进来的用例数据保存起来
第二步:遍历用例数据,每遍历一条数据 就动态的给测试类添加一个用例方法。
ddt 中的两个装饰器其实实现的就是这么两个步骤:
@data:做的是第一步将传入测试数据保存起来;
@ddt 做的是第二步,遍历用例数据,给测试类动态添加用例方法。
2、data 装饰器的实现
前面我们说到 data 这个装饰器,做的事情是将用例数据保存起来。
那么如何保存呢?其实最简单的方式就是保存被装饰的这个用例方法的属性。
接下来我们来具体实现:
先看一个 ddt 使用的案例
@ddt
class TestLogin(unittest.TestCase):@data(11,22)def test_login(self, item):pass
了解过装饰器装饰器原理的小伙伴,应该都知道上面@data(11,22) 这行代码执行的效果等同于
test_login = data(11,22)(test_login)
接下来我们来分析一下上面这行代码,首先是调用 data 这个装饰器函数,把用例数据 11,22 当成参数传入进去,然后返回一个可调用对象(函数),再次调用返回的函数并把用例方法传入进去。明确了调用的流程,那么我们就可以结合之前的需求去定义 data 这个装饰器函数了。
具体实现如下:
def data(*args):def wrapper(func):setattr(func, "PARAMS", args)return funcreturn wrapper
代码解读:
前面的案例在使用 data 时,执行的 test_login = data(11,22)(test_login)
先调用 data 传入的 11,22 通过不定长参数 args 接收,然后返回嵌套的函数 wrapper
然后调用返回的 wrapper 函数,传入被装饰的 test_login 方法
在 wrapper 函数中我们把用例数据保存为 test_login 这个方法的 PARAMS 属性,再把 test_login 返回
到此为止,data 这个装饰器我们就实现用例数据的保存
3、ddt 装饰器的实现
通过 data 这个装饰器我们实现了用例数据保存之后,我们接下来实现 ddt 这个装饰器,根据用例数据生成测试用例。前面的案例 @ddt 装饰测试类的时候,实际上执行的效果等同于下面的代码
TestLogin = ddt(TestLogin)
这行代码就是把被装饰器的类传入到 ddt 这个装饰器函数中,再把返回值赋值给 TestLogin。之前我们分析的时候说了 ddt 这个装饰器做的事情是遍历用例数据,动态的给测试类添加用例方法。
接下来我们就来实现 ddt 这个装饰器内部的逻辑。
def ddt(cls):for name, func in list(cls.__dict__.items()):if hasattr(func, "PARAMS"):for index, case_data in enumerate(getattr(func, "PARAMS")):new_test_name ="{}_{}".format(name,index)setattr(cls, new_test_name, func)else:delattr(cls, name)return cls
代码解读:
ddt 函数内部逻辑说明:
1、调用 ddt 这个函数时会把测试类当成参数传入进来,
2、然后通过
cls.__dict__
获取测试的所有属性和方法,进行遍历3、判断变量出来的属性或方法 有没有 PARAMS 这个属性,
4、如果有,则说明这个方法用 data 装饰器装饰过并传入了用例数据。
5、通过 getattr(func, "PARAMS")获取所有的用例数据,进行遍历。
6、每遍历出来一组用例数据,生产一个用例方法名, 再动态的给测试类添加一个用例方法。
7、遍历完所有用例数据之后,删除测试类原来定义的测试方法
8、最后返回测试类
当目前为止 ddt 和 data 这两个装饰器函数的基本功能实现了,可以自动根据用例数据生成测试用例了,接下来我们写个测试类来检查一下
# 定义装饰器函数data
def data(*args):def wrapper(func):setattr(func, "PARAMS", args)return funcreturn wrapper# 定义装饰器函数ddt
def ddt(cls):for name, func in list(cls.__dict__.items()):if hasattr(func, "PARAMS"):for index, case_data in enumerate(getattr(func, "PARAMS")):new_test_name = "{}_{}".format(name, index)setattr(cls, new_test_name, func)else:delattr(cls, name)return clsimport unittest# 编写测试类
@ddt
class TestDome(unittest.TestCase):@data(11, 22, 33, 44)def test_demo(self):pass
运行上述用例,我们就会发现执行了四条用例,根据用例数据生成用例的功能就已经实现了。
4、解决用例参数传递的问题
虽然上面基本的功能已经实现了,但是还存在一个问题:用例的数据没有传递到用例方法中。那么用例数据传递怎么实现了,我们可以通过一个闭包函数对用例方法进行修,从而实现在调用用例方法的时候,把用例测试当成参数传递进去。
修改原有用例方法的函数代码如下
from functools import wrapsdef update_test_func(test_func,case_data):@wraps(test_func)def wrapper(self):return test_func(self, case_data)return wrapper
代码解读:
上面我们定义了一个叫做 update_test_func 的闭包函数
闭包函数接收两个参数:test_func(接收用例方法),case_data(接收用例数据)
闭包函数返回一个嵌套函数,嵌套函数内部调用原来的用例方法,并传入测试数据
嵌套函数在定义时,使用了 functools 模块中的装饰器 wraps 来装饰,它可以让 wrapper 这个嵌套函数具有 test_func 这个用例函数的相关属性。
下面我们回到前面写的 ddt 这个函数中,在给测试类添加用例之前,调用 update_test_func 方法对用例方法进行修改:
def ddt(cls):for name, func in list(cls.__dict__.items()):if hasattr(func, "PARAMS"):for index, case_data in enumerate(getattr(func, "PARAMS")):# 生成一个用例方法名new_test_name = "{}_{}".format(name, index)# 修改原有的测试方法,设置用例数据为测试方法的参数test_func = update_test_func(func,case_data)setattr(cls, new_test_name, test_func)else:delattr(cls, name)return cls
通过加上这一步之后,我们在测试类中 动态给测试类添加的测试方法,其实指向的全部是 update_test_func 里面定义的 wrapper 函数,在执行测试用的时候实际上也是执行的 wrapper 函数,而在 wrapper 函数内部,我们调用了原来定义的测试方法,并将用例数据传入了进去。
到此为止 ddt 的功能我们就完全实现了!
End:
给大家举一个完整的案例,大家可以复制过去运行,也可以自己去写一遍,还可以根据自己的一些需求进行自定义的扩展。
完整案例
from functools import wraps
import unittest# --------ddt的实现--------
def data(*args):def wrapper(func):setattr(func, "PARAMS", args)return funcreturn wrapperdef update_test_func(test_func, case_data):@wraps(test_func)def wrapper(self):return test_func(self, case_data)return wrapperdef ddt(cls):for name, func in list(cls.__dict__.items()):if hasattr(func, "PARAMS"):for index, case_data in enumerate(getattr(func, "PARAMS")):# 生成一个用例方法名new_test_name = "{}_{}".format(name, index)# 修改原有的测试方法,设置用例数据为测试方法的参数test_func = update_test_func(func, case_data)setattr(cls, new_test_name, test_func)else:delattr(cls, name)return cls# --------测试用例编写--------
@ddt
class TestDome(unittest.TestCase):@data(11, 22, 33, 44)def test_demo(self, data):assert data < 40
#---------用例执行-----------
unittest.main()
相关文章:

python 自动化福音,30行代码手撸ddt模块
用 python 做过自动化的小伙伴,大多数都应该使用过 ddt 这个模块,不可否认 ddt 这个模块确实挺好用,可以自动根据用例数据,来生成测试用例,能够很方便的将测试数据和测试用例执行的逻辑进行分离。 接下来就带大家一起…...

基于GATK流程化进行SNP calling
在进行变异检测时,以群体基因组重测序数据为例,涉及到的个体基本都是上百个,而其中大多数流程均是重复的步骤。 本文将基于GATK进行SNP calling的流程写入循环,便于批量分析。 1 涉及变量 1.工作目录work_dir/ 2.参考基因组ref…...

【Java SE】如何解读Java的继承和多态的特性?
前言 什么是继承和多态,为什么被称为Java三大特性,这两大特性为我们程序员带来了什么影响呢?是让复杂的代码简化了,还是为程序员写代码提供了多样性呢?那让我们一起来揭开这层神秘的面纱吧! 1.继承 1.1为…...

uniapp 手动调用form表单submit事件
背景: UI把提交的按钮弄成了图片,之前的button不能用了。 <button form-type"submit">搜索</button> 实现: html: 通过 this.$refs.fd 获取到form的vue对象。手动调用里面的_onSubmit()方法。 methods:…...

11月20日星期一今日早报简报微语报早读
11月20日星期一,农历十月初八,早报微语早读。 1、T1以3-0横扫WBG,拿下S13冠军!Faker豪取第4冠; 2、天舟七号货运飞船已运抵文昌发射场,将于明年初发射; 3、“中韩之战”球票已经售罄…...

Unity中 Start和Awake的区别
Awake和Start在Unity中都是MonoBehaviour脚本中的生命周期函数 Awake函数在游戏对象首次被加载时调用,在游戏对象初始化之前调用。 start函数在游戏对象初始化完成后调用,在update第一次执行前调用。 这两个函数在其生命周期内都只会调用一次…...
进度条、git常见指令以及gdb的常用指令
进度条 进度条是笔者所接触的第一个更加贴近于系统的小玩意,主要是要理解回车、换行、换行回车和缓冲区的概念。 回车是回到当前行的第一个光标位置,换行是换到下一行但是光标还在原来的位置,换行回车就是键盘上面的回车键是回到下一行的第…...
ubuntu20编译安装pkg-config
从下载到安装的步骤如下: wget https://pkg-config.freedesktop.org/releases/pkg-config-0.29.tar.gztar -zxvf pkg-config-0.29.tar.gzcd pkg-config-0.29/./configure --with-internal-glibsudo makesudo make checksudo make install make过程中可能会遇到的问题&#x…...
奇富科技发布鸿蒙元服务1.0版本,打造鸿蒙生态金融科技全新体验
近日,奇富科技率先发布鸿蒙元服务1.0版本,成为首家融入鸿蒙生态的金融科技公司,为用户带来前所未有的数字生活体验。此次与华为终端云的全面合作,是两大行业领军者的深度融合,不仅实现技术的交融,更彰显两大…...

【Git学习一】初始化仓库git init的使用和提交git add与git commit的使用
😁 作者简介:一名大四的学生,致力学习前端开发技术 ⭐️个人主页:夜宵饽饽的主页 ❔ 系列专栏:Git等软件工具技术的使用 👐学习格言:成功不是终点,失败也并非末日,最重要…...

Redux-状态管理组件
一、简介 react中的状态只属于某个组件。而Redux是一个全局管理js状态的架构,让组件通信更加容易。 之前是状态在所有组件间传递,而redux通过store来实现这个功能。 Redux特性: 1.Single source Of truth,通过store唯一维护状态…...

【bigo前端】egret中的对象池浅谈
本文首发于:https://github.com/bigo-frontend/blog/ 欢迎关注、转载。 egret是一款小游戏开发引擎,支持跨平台开发,之前使用这款引擎开发了一款捕鱼游戏,在这里简单聊下再egret中关于对象池的使用,虽然该引擎已经停止…...

用公式告诉你 现货黄金投资者要不要换策略?
看过笔者相关文章的朋友都知道,其实笔者是相当不鼓励投资者更改策略的。但这并不意味着,策略不能改或者换。之所以反对更改策略,是因为很多人对自己的策略还没上手,没了解清楚就急着换策略,这是没必要的。通过下面这个…...

系列六、多线程集合不安全
一、多线程List集合不安全 1.1、List集合不安全案例代码 /*** Author : 一叶浮萍归大海* Date: 2023/11/20 12:38* Description: 多线层环境下List集合不安全案例代码*/ public class NotSafeListMainApp {public static void main(String[] args) {List<String> list …...
MidJourney笔记(1)-入门
注册 MidJourney注册和使用方式,有点特别。在介绍注册之前,需要给大家先介绍Discord。 Discord是一家游戏聊天应用与社区,在国内用的人相对比较少,在国外用得比较多。 那MidJourney和Discord有什么关系呢? MidJourney是搭建在Discord上的一个人工智能程序,通过在Discord添…...

CRM系统定制开发价格
我们都知道,CRM系统对企业有着很大的帮助。但是市面上大多数CRM系统都是标准化的,无法满足那些产品线复杂,或者有着特殊需求的企业。这个时候,就需要对CRM系统进行二次开发。那么,CRM系统二次开发的价格是多少…...
Kubernetes实战(五)-pod之间网络请求实战
1 同namespace内pod网络请求 1.1 创建namespace ygq $ kubectl create namespace ygq namespace/ygq created 1.2 创建svc和deployment 在naemspace ygq下创建两个应用:nginx和nginx-test。 1.2.1 部署应用nginx $ cat nginx-svc.yaml apiVersion: v1 kind: …...

7年经验之谈 —— 如何高效的开展app的性能测试?
APP性能测试是什么 从网上查了一下,貌似也没什么特别的定义,我这边根据自己的经验给出一个自己的定义,如有巧合纯属雷同。 客户端性能测试就是,从业务和用户的角度出发,设计合理且有效的性能测试场景,制定…...

小程序action-sheet结合自定义tabbar显示
要实现此效果,遇到的问题:背景在电脑端调试的情况正常的情况下,手机端点击事件工单,返回回来的时候action-sheet卡住在屏幕上,点击遮罩层都不消失。更奇怪的是 这种情况并不是每次发生,而是有时候发生&…...
机器学习笔记 - 隐马尔可夫模型的简述
隐马尔可夫模型是一个并不复杂的数学模型,到目前为止,它一直被认为是解决大多数自然语言处理问题最为快速、有效的方法。它成功地解决了复杂的语音识别、机器翻译等问题。看完这些复杂的问题是如何通过简单的模型得到描述和解决,我们会由衷地感叹数学模型之妙。 人类信息交流…...
算法刷题-回溯
今天给大家分享的还是一道关于dfs回溯的问题,对于这类问题大家还是要多刷和总结,总体难度还是偏大。 对于回溯问题有几个关键点: 1.首先对于这类回溯可以节点可以随机选择的问题,要做mian函数中循环调用dfs(i&#x…...
使用python进行图像处理—图像滤波(5)
图像滤波是图像处理中最基本和最重要的操作之一。它的目的是在空间域上修改图像的像素值,以达到平滑(去噪)、锐化、边缘检测等效果。滤波通常通过卷积操作实现。 5.1卷积(Convolution)原理 卷积是滤波的核心。它是一种数学运算,…...

npm安装electron下载太慢,导致报错
npm安装electron下载太慢,导致报错 背景 想学习electron框架做个桌面应用,卡在了安装依赖(无语了)。。。一开始以为node版本或者npm版本太低问题,调整版本后还是报错。偶尔执行install命令后,可以开始下载…...

Ubuntu 安装 Mysql 数据库
首先更新apt-get工具,执行命令如下: apt-get upgrade安装Mysql,执行如下命令: apt-get install mysql-server 开启Mysql 服务,执行命令如下: service mysql start并确认是否成功开启mysql,执行命令如下&am…...
OCC笔记:TDF_Label中有多个相同类型属性
注:OCCT版本:7.9.1 TDF_Label中有多个相同类型的属性的方案 OCAF imposes the restriction that only one attribute type may be allocated to one label. It is necessary to take into account the design of the application data tree. For exampl…...
DOM(文档对象模型)深度解析
DOM(文档对象模型)深度解析 DOM 是 HTML/XML 文档的树形结构表示,提供了一套让 JavaScript 动态操作网页内容、结构和样式的接口。 一、DOM 核心概念 1. 节点(Node)类型 类型值说明示例ELEMENT_NODE1元素节点<div>, <p>TEXT_NODE3文本节点元素内的文字COMMEN…...

leetcode238-除自身以外数组的乘积
leetcode 238 思路 可以在不使用除法的情况下,利用前缀积和后缀积来实现解答 前缀积:对每个位置,计算当前数字左侧的所有数字的乘积后缀积:对每个位置,计算当前数字右侧的所有数字的乘积 结合这两种思想࿰…...

HarmonyOS Next 弹窗系列教程(3)
HarmonyOS Next 弹窗系列教程(3) 选择器弹窗 (PickerDialog) 介绍 选择器弹窗通常用于在用户进行某些操作(如点击按钮)时显示特定的信息或选项。让用户可以进行选择提供的固定的内容。 以下内容都属于选择器弹窗: …...
易语言是什么?易语言能做什么?
易语言(EPL)是什么? 易语言(Easy Programming Language,简称EPL)是一款面向中文用户的编程语言,由中国人吴涛于2000年开发,专为降低编程门槛设计。其核心特点是…...

AT_abc409_e [ABC409E] Pair Annihilation
AT_abc409_e [ABC409E] Pair Annihilation 赛时没开longlong挂了。 思路 首先我们可以把这棵树转化为一颗有根树,且所有电子的都朝根节点移动。 那么接下来我们就需要选择一个最优的树根。 考虑换根dp。 但是可以发现换根时答案其实是没有变化的。 我们设 f…...