如何从0构建一款类似pytest的工具
Pytest主要模块
Pytest 是一个强大且灵活的测试框架,它通过一系列步骤来发现和运行测试。其核心工作原理包括以下几个方面:
测试发现:Pytest 会遍历指定目录下的所有文件,找到以 test_ 开头或 _test.py 结尾的文件,并且识别文件中以 test_ 开头的函数作为测试函数。
测试收集:Pytest 使用 Python 的 inspect 模块和标准命名约定来收集测试函数。它会导入测试模块,并检查模块中的所有函数,找到符合测试命名约定的函数。
测试执行:Pytest 运行收集到的测试函数,并捕获测试的结果,包括成功、失败、错误等信息。
结果报告:Pytest 格式化并输出测试结果,包括每个测试的通过、失败、错误信息。
Pytest 命令运行测试的机制,当执行 pytest 命令时,以下是发生的主要步骤:
命令行入口:Pytest 的入口函数会从命令行参数中解析出测试路径和其他选项。
初始化:Pytest 初始化内部组件,包括配置、插件等。
测试发现和收集:根据配置和路径进行测试发现和收集。
测试执行:逐个运行收集到的测试函数,并记录结果。
结果报告:汇总并输出测试结果。
从0构建一个类似pytest的工具
前面简要介绍了pytest的主要功能模块,如果要从0构建一个类似pytest的工具,应该如何实现呢?下面是实现的具体代码。
import os
import importlib.util
import inspect
import traceback
import argparse# 发现测试文件
def discover_tests(start_dir):test_files = []for root, _, files in os.walk(start_dir):for file in files:if file.startswith('test_') and file.endswith('.py'):test_files.append(os.path.join(root, file))return test_files# 查找测试函数
def find_test_functions(module):test_functions = []for name, obj in inspect.getmembers(module):if inspect.isfunction(obj) and name.startswith('test_'):test_functions.append(obj)return test_functions# 运行测试函数
def run_tests(test_functions):results = []for test_func in test_functions:result = {'name': test_func.__name__}try:test_func()result['status'] = 'pass'except AssertionError as e:result['status'] = 'fail'result['error'] = traceback.format_exc()except Exception as e:result['status'] = 'error'result['error'] = traceback.format_exc()results.append(result)return results# 打印测试结果
def print_results(results):for result in results:print(f'Test: {result["name"]} - {result["status"]}')if result.get('error'):print(result['error'])print('-' * 40)# 主函数
if __name__ == '__main__':parser = argparse.ArgumentParser(description='A simple pytest-like tool')parser.add_argument('test_path', type=str, help='Path to the test file or directory')args = parser.parse_args()test_path = args.test_pathif os.path.isdir(test_path):test_files = discover_tests(test_path)elif os.path.isfile(test_path):test_files = [test_path]else:print(f"Invalid path: {test_path}")exit(1)for test_file in test_files:# 根据测试文件路径创建模块规范spec = importlib.util.spec_from_file_location("module.name", test_file)# 根据模块规范创建一个模块对象module = importlib.util.module_from_spec(spec)# 加载并执行模块代码spec.loader.exec_module(module)# 在模块中查找测试函数test_functions = find_test_functions(module)# 运行所有找到的测试函数,并记录结果results = run_tests(test_functions)# 输出测试结果print_results(results)
准备测试脚本文件:test_example.py,内容如下所示:每个测试方法都是以test开头,这样上面的代码才能正确捕获到测试方法。
def test_addition():assert 1 + 1 == 2def test_subtraction():assert 2 - 1 == 1def test_failure():assert 1 + 1 == 3
执行命令"python3 simple_pytest.py test_example.py",运行测试,结果如下:两个执行成功,一个失败。说明整个工具功能符合预期。
importlib.util包
上面的代码通过importlib.util来动态加载和操作模块,importlib.util的主要作用是提供实用工具来帮助开发者在运行时动态加载模块,而不是在编译时静态加载。这对于需要在程序执行期间动态加载模块的场景非常有用,例如插件系统、测试框架等。提供的主要方法有:
spec_from_file_location(name, location, *, loader=None, submodule_search_locations=None): 根据文件路径创建一个模块规范 (ModuleSpec)
module_from_spec(spec):根据模块规范创建一个新的模块对象
spec.loader.exec_module(module):执行加载模块的代码,将代码注入到模块对象中
find_spec(name, package=None):查找指定名称的模块规范
模块规范具体包含哪些属性呢?模块规范主要包含模块名称,模块的加载器,模块的来源,是否有文件路径,子模块搜索路径,缓存路径,是否有父模块。
下面这段代码演示了如何通过importlib.util包来创建模块,并调用模块中的函数。
import importlib.util
# 获取模块文件路径
file_path = "example_module.py"
# 创建模块规范对象
spec = importlib.util.spec_from_file_location("example_module", file_path)
# 打印ModuleSpec对象的信息
print("ModuleSpec Information:")
print(f"Name: {spec.name}")
print(f"Loader: {spec.loader}")
print(f"Origin: {spec.origin}")
print(f"Has Location: {spec.has_location}")
print(f"Submodule Search Locations: {spec.submodule_search_locations}")
# 创建模块对象
module = importlib.util.module_from_spec(spec)
# 加载并执行模块
spec.loader.exec_module(module)
# 调用模块中的函数
module.hello()
module.test_addition()
module.test_failure()
example_module.py测试文件内容
def hello():print("Hello from example_module!")def test_addition():assert 1 + 1 == 2 def test_failure():assert 1 + 1 == 3
执行结果如下所示:可以看到文件中的函数都被执行了,且给出了执行结果。如果是测试框架,就可以收集这些测试结果,用户后续的测试报告显示。
自定义命令运行测试文件
前面在执行测试的时候,是通过python命令来执行测试文件的,如果要像pytest一样,通过自定义命令来执行测试文件,应该如何实现呢?这里需要借助Python的setuptools包中的 entry_points 功能。通过定义一个控制台脚本,让用户直接通过命令行运行工具。在原来代码基础上,创建setup.py文件。entry_points中console_scripts中,定义了自定义命令是my_pytests,对应的代码入口是之前的工具实现文件simple_pytest文件中main方法。
from setuptools import setup, find_packagessetup(name='my_pytest',version='0.1',packages=find_packages(),entry_points={'console_scripts': ['my_pytests=simple_pytest:main',],},python_requires='>=3.6',
)
定义好setup文件后,通过命令进行打包"pip install -e .",就可以通过my_pytests命令执行文件了,例如“my_pytests ./test_example.py” or "my_pytests ./tests".执行结果如下所示:
以上就是构建类似pytest工具的实现过程以及原理。
相关文章:
如何从0构建一款类似pytest的工具
Pytest主要模块 Pytest 是一个强大且灵活的测试框架,它通过一系列步骤来发现和运行测试。其核心工作原理包括以下几个方面:测试发现:Pytest 会遍历指定目录下的所有文件,找到以 test_ 开头或 _test.py 结尾的文件,并且…...
6.27-6.29 旧c语言
#include<stdio.h> struct stu {int num;float score;struct stu *next; }; void main() {struct stu a,b,c,*head;//静态链表a.num 1;a.score 10;b.num 2;b.score 20;c.num 3;c.score 30;head &a;a.next &b;b.next &c;do{printf("%d,%5.1f\n&…...
Unidbg调用-补环境V3-Hook
结合IDA和unidbg,可以在so的执行过程进行Hook,这样可以让我们了解并分析具体的执行步骤。 应用场景:基于unidbg调试执行步骤 或 还原算法(以Hookzz为例)。 1.大姨妈 1.1 0x1DA0 public void hook1() {...
从AICore到TensorCore:华为910B与NVIDIA A100全面分析
华为NPU 910B与NVIDIA GPU A100性能对比,从AICore到TensorCore,展现各自计算核心优势。 AI 2.0浪潮汹涌而来,若仍将其与区块链等量齐观,视作炒作泡沫,则将错失新时代的巨大机遇。现在,就是把握AI时代的关键…...
Edge 浏览器退出后,后台占用问题
Edge 浏览器退出后,后台占用问题 环境 windows 11 Microsoft Edge版本 126.0.2592.68 (正式版本) (64 位)详情 在关闭Edge软件后,查看后台,还占用很多系统资源。实在不明白,关了浏览器还不能全关了,微软也学流氓了。…...
实验八 T_SQL编程
题目 以电子商务系统数据库ecommerce为例 1、在ecommerce数据库,针对会员表member首先创建一个“呼和浩特地区”会员的视图view_hohhot,然后通过该视图查询来自“呼和浩特”地区的会员信息,用批处理命令语句将问题进行分割,并分…...
【爆肝34万字】从零开始学Python第2天: 判断语句【入门到放弃】
目录 前言判断语句True、False简单使用作用 比较运算符引入比较运算符的分类比较运算符的结果示例代码总结 逻辑运算符引入逻辑运算符的简单使用逻辑运算符与比较运算符一起使用特殊情况下的逻辑运算符 if 判断语句引入基本使用案例演示案例补充随堂练习 else 判断子句引入else…...
React 19 新特性集合
前言:https://juejin.cn/post/7337207433868197915 新 React 版本信息 伴随 React v19 Beta 的发布,React v18.3 也一并发布。 React v18.3相比最后一个 React v18 的版本 v18.2 ,v18.3 添加了一些警告提示,便于尽早发现问题&a…...
耐高温水位传感器有哪些
耐高温水位传感器在现代液位检测技术中扮演着重要角色,特别适用于需要高温环境下稳定工作的应用场合。这类传感器的设计和材质选择对其性能和可靠性至关重要。 一种典型的耐高温水位传感器是FS-IR2016D,它采用了PPSU作为主要材质。PPSU具有优良的耐高温…...
Symfony国际化与本地化:打造多语言应用的秘诀
标题:Symfony国际化与本地化:打造多语言应用的秘诀 摘要 Symfony是一个高度灵活的PHP框架,用于创建Web应用程序。它提供了强大的国际化(i18n)和本地化(l10n)功能,允许开发者轻松创…...
ApolloClient GraphQL 与 ReactNative
要在 React Native 应用程序中设置使用 GraphQL 的简单示例,您需要遵循以下步骤: 设置一个 React Native 项目。安装 GraphQL 必要的依赖项。创建一个基本的 GraphQL 服务器(或使用公共 GraphQL 端点)。从 React Native 应用中的…...
【贡献法】2262. 字符串的总引力
本文涉及知识点 贡献法 LeetCode2262. 字符串的总引力 字符串的 引力 定义为:字符串中 不同 字符的数量。 例如,“abbca” 的引力为 3 ,因为其中有 3 个不同字符 ‘a’、‘b’ 和 ‘c’ 。 给你一个字符串 s ,返回 其所有子字符…...
C#基于SkiaSharp实现印章管理(3)
本系列第一篇文章中创建的基本框架限定了印章形状为矩形,但常用的印章有方形、圆形等多种形状,本文调整程序以支持定义并显示矩形、圆角矩形、圆形、椭圆等4种形式的印章背景形状。 定义印章背景形状枚举类型,矩形、圆形、椭圆相关的尺寸…...
如何理解泛型的编译期检查
既然说类型变量会在编译的时候擦除掉,那为什么我们往 ArrayList 创建的对象中添加整数会报错呢?不是说泛型变量String会在编译的时候变为Object类型吗?为什么不能存别的类型呢?既然类型擦除了,如何保证我们只能使用泛型…...
计算机组成原理:海明校验
在上图中,对绿色的7比特数据进行海明校验,需要添加紫色的4比特校验位,总共是蓝色的11比特。紫色的校验位pi分布于蓝色的hi的1, 2, 4, 8, 16, 32, 64位,是2i-1位。绿色的数据位bi分布于剩下的位。 在下图中,b1位于h3&a…...
信息学奥赛初赛天天练-39-CSP-J2021基础题-哈夫曼树、哈夫曼编码、贪心算法、满二叉树、完全二叉树、前中后缀表达式转换
PDF文档公众号回复关键字:20240629 2022 CSP-J 选择题 单项选择题(共15题,每题2分,共计30分:每题有且仅有一个正确选项) 5.对于入栈顺序为a,b,c,d,e的序列,下列( )不合法的出栈序列 A. a,b&a…...
第11章 规划过程组(收集需求)
第11章 规划过程组(一)11.3收集需求,在第三版教材第377~378页; 文字图片音频方式 第一个知识点:主要输出 1、需求跟踪矩阵 内容 业务需要、机会、目的和目标 项目目标 项目范围和 WBS 可…...
探索WebKit的守护神:深入Web安全策略
探索WebKit的守护神:深入Web安全策略 在数字化时代,网络已成为我们生活的一部分,而网页浏览器作为我们探索网络世界的窗口,其安全性至关重要。WebKit作为众多流行浏览器的内核,例如Safari,其安全性策略是保…...
unity ScrollRect裁剪ParticleSystem粒子
搜了下大概有这几种方法 通过模板缓存通过shader裁剪区域:案例一,案例二,案例三,三个案例都是类似的方法,需要在c#传入数据到shader通过插件 某乎上的模板缓存方法link,(没有登录看不到全文&a…...
凤仪亭 | 第7集 | 大丈夫生居天地之间,岂能郁郁久居人下 | 司徒一言,令我拨云见日,茅塞顿开 | 三国演义 | 逐鹿群雄
🙋大家好!我是毛毛张! 🌈个人首页: 神马都会亿点点的毛毛张 📌这篇博客分享的是《三国演义》文学剧本第Ⅰ部分《群雄逐鹿》的第7️⃣集《凤仪亭》的经典语句和文学剧本全集台词 文章目录 1.经典语句2.文学剧本台词 …...
React实战学习(一)_棋盘设计
需求: 左上侧:状态左下侧:棋盘,保证胜利就结束 和 下过来的不能在下右侧:“时光机”,保证可以回顾,索引 语法: 父子之间属性传递(props)子父组件传递(写法上&…...
【LeetCode】每日一题:三数之和
解题思路 最开始是打算沿着二数之和的思路做,即固定了最大的,然后小的开始遍历,因为这种遍历方式只需要遍历一轮就能完成,所以复杂度应该是O(n2),但是最后几个示例还是超时了,可能进…...
逆风而行:提升逆商,让困难成为你前进的动力
一、引言 生活,总是充满了未知与变数。有时,我们会遇到阳光明媚的日子,享受着宁静与和谐;但更多时候,我们却不得不面对那些突如其来的坏事件,如工作的挫折、人际关系的困扰、健康的挑战等。这些事件如同突…...
新能源汽车CAN总线故障定位与干扰排除的几个方法
CAN总线是目前最受欢迎的现场总线之一,在新能源车中有广泛应用。新能源车的CAN总线故障和隐患将影响驾驶体验甚至行车安全,如何进行CAN总线故障定位及干扰排除呢? 目前,国内机动车保有量已经突破三亿大关。由于大量的燃油车带来严峻的环境问题,因此全面禁售燃油车的日程在…...
【涵子来信】——社交宝典:克服你心中的内向,世界总有缺陷
内向,你是内向的吗?想必每个人不同,面对的情形也是不同的。 暑假是一个很好的机会,我是可以去多社交社交。但是,面对着CSDN上这么多技术人er,那么,我的宝典,对于大家,有…...
LabVIEW项目外协时选择公司与个人兼职的比较
在选择LabVIEW项目外协合作伙伴时,外协公司和个人兼职各有优劣。个人兼职成本较低且灵活,但在可靠性、技术覆盖面、资源和风险管理上存在不足。而外协公司拥有专业团队、丰富资源、完善的项目管理和风险控制,尽管成本较高,但能提…...
汽车电子工程师入门系列——CAN 规范系列通读
我是穿拖鞋的汉子,魔都中坚持长期主义的汽车电子工程师。 老规矩,分享一段喜欢的文字,避免自己成为高知识低文化的工程师: 屏蔽力是信息过载时代一个人的特殊竞争力,任何消耗你的人和事,多看一眼都是你的不对。非必要不费力证明自己,无利益不试图说服别人,是精神上的节…...
泽众云真机-平台华为机型HarmonyOS NEXT系统已上线!
泽众云真机平台华为机型HarmonyOS NEXT系统已上线! 之前文章《泽众云真机-平台即将升级支持华为机型HarmonyOS NEXT系统泽众云真机-平台即将升级支持华为机型HarmonyOS NEXT系统》,为什么要升级HarmonyOS NEXT系统?我们之前有说过,…...
AI基础:从线性回归到梯度下降
一个简单的问题: 如果此时你正站在迷路缭绕的山坡上,能见度不高,但是你又想去往最低的山谷的位置,怎么走? 很简单,哪里陡那就往那里走呗——而这就是梯度下降算法的思想。 古话说:“先发制于人…...
AI产品经理面试
把优秀当习惯把优秀当习惯肯定不是口头说说,那有什么判断标准吗? 当我做完一件事儿的时候,我会看它有没有突破我的舒适圈、能不能惊艳到我自己。这就是我的判断标准。 在自我介绍和经历介绍时,面试者应该注重以下几个方面…...
建筑新型模板/关键词优化是什么意思?
1、关闭防火墙service iptables stop关闭SELinuxsetenforce 02、安装 Sambayum install -y samba samba-calient3、创建用户useradd testsamba身份认证pdbedit -a test4、设置密码:passwd testuser4、启动samba 服务添加开机启动 chkconfig smb on立即启动 service …...
怎么做网站免费/怎样联系百度客服
网络互联网络互联通常是指交换机和路由器之间的互联,因为交换机和交换机互联存在三方面的不足:1、广播风暴,2、网络互联,3、网络安全。所以通常需要添加路由器,以有效的放置上述三种问题。路由器根据IP地址来区别不同网…...
开发软件下载网站/解释seo网站推广
最关键的部分是计算序列的同时,不会引发无限递归, #:: 表达式的右边只有在被请求时才会被求值。转载于:https://www.cnblogs.com/lianghong881018/p/11193358.html...
wordpress邮件订阅/定制网站建设推广服务
JS运动基础框架 1 注意:在开始运动时关闭已有的定时器 2 注意:把运动和停止隔开 <!DOCTYPE html> <html><head><meta charset"utf-8"><title></title><style type"text/css">#div1{wi…...
县级新闻网站建设/网络营销公司
终于成功地在linux上面装上mysql了。兴奋之余写了这篇东西来纪念一下,顺便做个笔记,以便以后遇到问题时可以查阅。首先看看安装需要用到的所有的包吧。[rootlocalhost6 mysql]# lsmysql-5.0.37-2.fc7.i386.rpm mysql-server-5.0.37-2.fc7.i386.rpm unixO…...
淄博网站建设讲解透彻/济南今日头条最新消息
CSS规范 - 分类方法 SS文件的分类和引用顺序 通常,一个项目我们只引用一个CSS,但是对于较大的项目,我们需要把CSS文件进行分类。 我们按照CSS的性质和用途,将CSS文件分成“公共型样式”、“特殊型样式”、“皮肤型样式”ÿ…...