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卡住在屏幕上,点击遮罩层都不消失。更奇怪的是 这种情况并不是每次发生,而是有时候发生&…...
机器学习笔记 - 隐马尔可夫模型的简述
隐马尔可夫模型是一个并不复杂的数学模型,到目前为止,它一直被认为是解决大多数自然语言处理问题最为快速、有效的方法。它成功地解决了复杂的语音识别、机器翻译等问题。看完这些复杂的问题是如何通过简单的模型得到描述和解决,我们会由衷地感叹数学模型之妙。 人类信息交流…...
BetterJoy终极指南:让Switch手柄在Windows上完美运行
BetterJoy终极指南:让Switch手柄在Windows上完美运行 【免费下载链接】BetterJoy Allows the Nintendo Switch Pro Controller, Joycons and SNES controller to be used with CEMU, Citra, Dolphin, Yuzu and as generic XInput 项目地址: https://gitcode.com/g…...
Omni-Vision Sanctuary在嵌入式边缘设备上的轻量化部署思考
Omni-Vision Sanctuary在嵌入式边缘设备上的轻量化部署思考 1. 嵌入式视觉的挑战与机遇 在智能摄像头、工业质检设备、无人机等嵌入式场景中,视觉模型的部署一直面临特殊挑战。传统方案要么性能不足,要么功耗过高,难以平衡实时性与能效比。…...
Ubuntu安装中文输入法后无法输入中文----问题分析及解决方法
问题:之前在Ubuntu系统上安装过搜狗输入法,且能正常输入中文。但重启之后无法调出,Shift切换也不管用,依旧是英文原因分析:后台进程(Fcitx)卡死或崩溃了解决方法:重启Fcitx输入法框架…...
C语言调用Omni-Vision Sanctuary轻量级推理接口(C API)教程
C语言调用Omni-Vision Sanctuary轻量级推理接口(C API)教程 1. 引言:为什么选择C API? 在嵌入式设备和资源受限的环境中,Python运行时往往显得过于臃肿。Omni-Vision Sanctuary提供的C语言接口(C API&…...
Phi-3-mini-4k-instruct-gguf入门必看:q4-GGUF量化对中文语义保留的影响实测
Phi-3-mini-4k-instruct-gguf入门必看:q4-GGUF量化对中文语义保留的影响实测 1. 模型简介 Phi-3-mini-4k-instruct-gguf是微软Phi-3系列中的轻量级文本生成模型GGUF版本,特别适合中文场景下的问答、文本改写、摘要生成等任务。这个经过量化的模型版本在…...
Spring Boot 基础学习笔记
Spring Boot 基础学习笔记 一、Spring Boot 概述 1. 定义 Spring Boot 是 Pivotal 团队基于 Spring 框架开发的快速开发脚手架,核心宗旨是简化 Spring 应用的初始化搭建和开发流程,通过「约定优于配置」的思想,大幅减少 XML 配置和繁琐的依…...
2026实测不踩坑!6款成品PPT网站客观测评
2026实测不踩坑!6款成品PPT网站客观测评作为常年深耕AI工具测评的博主,日常需应对各类PPT创作需求,也经常收到粉丝咨询相关工具选择。经过实测多款成品PPT网站后,整理出6款适配性较强的平台,涵盖不同需求场景ÿ…...
3步掌控《缺氧》存档:用Oni-Duplicity打造理想殖民地
3步掌控《缺氧》存档:用Oni-Duplicity打造理想殖民地 【免费下载链接】oni-duplicity A web-hosted, locally-running save editor for Oxygen Not Included. 项目地址: https://gitcode.com/gh_mirrors/on/oni-duplicity 你是否曾因《缺氧》中复制人负面特质…...
[Python3高阶编程] - 异步编程深度学习指南二: 同步原语
概述在 Python 异步编程中,虽然协程(coroutine)天然避免了线程切换开销,但多个协程仍可能同时访问共享资源(如全局变量、文件、数据库连接),从而引发竞态条件(Race Condition&#x…...
【JDK21虚拟线程生产就绪 checklist】:8类典型场景配置模板(WebFlux/Quarkus/Vert.x/RSocket全覆盖)
第一章:JDK21虚拟线程核心机制与生产就绪定义虚拟线程(Virtual Threads)是 JDK 21 中正式引入的里程碑特性(JEP 444),其本质是轻量级、用户态调度的 Java 线程抽象,由 JVM 在平台线程࿰…...
