当前位置: 首页 > news >正文

如何从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性能对比&#xff0c;从AICore到TensorCore&#xff0c;展现各自计算核心优势。 AI 2.0浪潮汹涌而来&#xff0c;若仍将其与区块链等量齐观&#xff0c;视作炒作泡沫&#xff0c;则将错失新时代的巨大机遇。现在&#xff0c;就是把握AI时代的关键…...

Edge 浏览器退出后,后台占用问题

Edge 浏览器退出后&#xff0c;后台占用问题 环境 windows 11 Microsoft Edge版本 126.0.2592.68 (正式版本) (64 位)详情 在关闭Edge软件后&#xff0c;查看后台&#xff0c;还占用很多系统资源。实在不明白&#xff0c;关了浏览器还不能全关了&#xff0c;微软也学流氓了。…...

实验八 T_SQL编程

题目 以电子商务系统数据库ecommerce为例 1、在ecommerce数据库&#xff0c;针对会员表member首先创建一个“呼和浩特地区”会员的视图view_hohhot&#xff0c;然后通过该视图查询来自“呼和浩特”地区的会员信息&#xff0c;用批处理命令语句将问题进行分割&#xff0c;并分…...

【爆肝34万字】从零开始学Python第2天: 判断语句【入门到放弃】

目录 前言判断语句True、False简单使用作用 比较运算符引入比较运算符的分类比较运算符的结果示例代码总结 逻辑运算符引入逻辑运算符的简单使用逻辑运算符与比较运算符一起使用特殊情况下的逻辑运算符 if 判断语句引入基本使用案例演示案例补充随堂练习 else 判断子句引入else…...

React 19 新特性集合

前言&#xff1a;https://juejin.cn/post/7337207433868197915 新 React 版本信息 伴随 React v19 Beta 的发布&#xff0c;React v18.3 也一并发布。 React v18.3相比最后一个 React v18 的版本 v18.2 &#xff0c;v18.3 添加了一些警告提示&#xff0c;便于尽早发现问题&a…...

耐高温水位传感器有哪些

耐高温水位传感器在现代液位检测技术中扮演着重要角色&#xff0c;特别适用于需要高温环境下稳定工作的应用场合。这类传感器的设计和材质选择对其性能和可靠性至关重要。 一种典型的耐高温水位传感器是FS-IR2016D&#xff0c;它采用了PPSU作为主要材质。PPSU具有优良的耐高温…...

Symfony国际化与本地化:打造多语言应用的秘诀

标题&#xff1a;Symfony国际化与本地化&#xff1a;打造多语言应用的秘诀 摘要 Symfony是一个高度灵活的PHP框架&#xff0c;用于创建Web应用程序。它提供了强大的国际化&#xff08;i18n&#xff09;和本地化&#xff08;l10n&#xff09;功能&#xff0c;允许开发者轻松创…...

ApolloClient GraphQL 与 ReactNative

要在 React Native 应用程序中设置使用 GraphQL 的简单示例&#xff0c;您需要遵循以下步骤&#xff1a; 设置一个 React Native 项目。安装 GraphQL 必要的依赖项。创建一个基本的 GraphQL 服务器&#xff08;或使用公共 GraphQL 端点&#xff09;。从 React Native 应用中的…...

【贡献法】2262. 字符串的总引力

本文涉及知识点 贡献法 LeetCode2262. 字符串的总引力 字符串的 引力 定义为&#xff1a;字符串中 不同 字符的数量。 例如&#xff0c;“abbca” 的引力为 3 &#xff0c;因为其中有 3 个不同字符 ‘a’、‘b’ 和 ‘c’ 。 给你一个字符串 s &#xff0c;返回 其所有子字符…...

C#基于SkiaSharp实现印章管理(3)

本系列第一篇文章中创建的基本框架限定了印章形状为矩形&#xff0c;但常用的印章有方形、圆形等多种形状&#xff0c;本文调整程序以支持定义并显示矩形、圆角矩形、圆形、椭圆等4种形式的印章背景形状。   定义印章背景形状枚举类型&#xff0c;矩形、圆形、椭圆相关的尺寸…...

如何理解泛型的编译期检查

既然说类型变量会在编译的时候擦除掉&#xff0c;那为什么我们往 ArrayList 创建的对象中添加整数会报错呢&#xff1f;不是说泛型变量String会在编译的时候变为Object类型吗&#xff1f;为什么不能存别的类型呢&#xff1f;既然类型擦除了&#xff0c;如何保证我们只能使用泛型…...

计算机组成原理:海明校验

在上图中&#xff0c;对绿色的7比特数据进行海明校验&#xff0c;需要添加紫色的4比特校验位&#xff0c;总共是蓝色的11比特。紫色的校验位pi分布于蓝色的hi的1, 2, 4, 8, 16, 32, 64位&#xff0c;是2i-1位。绿色的数据位bi分布于剩下的位。 在下图中&#xff0c;b1位于h3&a…...

信息学奥赛初赛天天练-39-CSP-J2021基础题-哈夫曼树、哈夫曼编码、贪心算法、满二叉树、完全二叉树、前中后缀表达式转换

PDF文档公众号回复关键字:20240629 2022 CSP-J 选择题 单项选择题&#xff08;共15题&#xff0c;每题2分&#xff0c;共计30分&#xff1a;每题有且仅有一个正确选项&#xff09; 5.对于入栈顺序为a,b,c,d,e的序列&#xff0c;下列( )不合法的出栈序列 A. a&#xff0c;b&a…...

第11章 规划过程组(收集需求)

第11章 规划过程组&#xff08;一&#xff09;11.3收集需求&#xff0c;在第三版教材第377~378页&#xff1b; 文字图片音频方式 第一个知识点&#xff1a;主要输出 1、需求跟踪矩阵 内容 业务需要、机会、目的和目标 项目目标 项目范围和 WBS 可…...

探索WebKit的守护神:深入Web安全策略

探索WebKit的守护神&#xff1a;深入Web安全策略 在数字化时代&#xff0c;网络已成为我们生活的一部分&#xff0c;而网页浏览器作为我们探索网络世界的窗口&#xff0c;其安全性至关重要。WebKit作为众多流行浏览器的内核&#xff0c;例如Safari&#xff0c;其安全性策略是保…...

unity ScrollRect裁剪ParticleSystem粒子

搜了下大概有这几种方法 通过模板缓存通过shader裁剪区域&#xff1a;案例一&#xff0c;案例二&#xff0c;案例三&#xff0c;三个案例都是类似的方法&#xff0c;需要在c#传入数据到shader通过插件 某乎上的模板缓存方法link&#xff0c;&#xff08;没有登录看不到全文&a…...

凤仪亭 | 第7集 | 大丈夫生居天地之间,岂能郁郁久居人下 | 司徒一言,令我拨云见日,茅塞顿开 | 三国演义 | 逐鹿群雄

&#x1f64b;大家好&#xff01;我是毛毛张! &#x1f308;个人首页&#xff1a; 神马都会亿点点的毛毛张 &#x1f4cc;这篇博客分享的是《三国演义》文学剧本第Ⅰ部分《群雄逐鹿》的第7️⃣集《凤仪亭》的经典语句和文学剧本全集台词 文章目录 1.经典语句2.文学剧本台词 …...

MAC地址失效下基于射频指纹的WiFi设备识别技术

1. 项目概述&#xff1a;当MAC地址失效时如何识别设备在当今的智慧城市和物联网环境中&#xff0c;WiFi设备识别技术面临着前所未有的挑战。传统依赖MAC地址的识别方法正逐渐失效——现代移动设备普遍采用MAC地址随机化技术&#xff0c;每次发送探测请求时都会生成虚拟MAC地址。…...

【Claude × Vue.js开发提效黑科技】:20年架构师亲测的5个AI辅助编码场景,90%开发者还不知道

更多请点击&#xff1a; https://intelliparadigm.com 第一章&#xff1a;Claude Vue.js开发提效黑科技全景图 智能提示与组件生成协同工作流 Claude 可深度理解 Vue 3 Composition API 的语义结构&#xff0c;配合 VS Code 的 Claude for VS Code 插件&#xff0c;开发者在…...

Linux下串口连接与CircuitPython开发实战指南

1. 项目概述&#xff1a;为什么串口是嵌入式开发的“生命线” 如果你玩过Arduino、树莓派Pico&#xff0c;或者正在捣鼓CircuitPython开发板&#xff0c;那么“串口”这个词对你来说一定不陌生。它就像一条看不见的数据管道&#xff0c;连接着你的电脑和那块小小的开发板。在W…...

别再一张张手动改了!用Python脚本批量解密微信PC版dat图片(附完整代码)

用Python自动化解密微信PC版dat图片的完整指南 微信PC版默认会将接收的图片保存为加密的dat文件格式&#xff0c;这些文件无法直接查看或使用。传统方法需要手动一张张转换&#xff0c;效率极低。本文将详细介绍如何用Python编写脚本&#xff0c;实现dat图片的批量自动解密&am…...

软件测试新人必看:入职前3个月如何快速站稳脚跟

对于软件测试新人而言&#xff0c;入职前3个月是职业发展的关键奠基期。这一阶段不仅要完成从校园到职场的身份转变&#xff0c;更要快速掌握专业技能、融入团队节奏&#xff0c;为后续的职业发展筑牢根基。本文将从专业角度出发&#xff0c;为软件测试新人拆解入职前3个月的成…...

教育 SaaS 供应链勒索攻击机理与闭环防御研究 —— 以 Canvas 数据泄露事件为例

摘要 2026 年 5 月&#xff0c;教育科技企业 Instructure 旗下 Canvas 学习管理系统遭遇 ShinyHunters 黑客组织攻击&#xff0c;3.65TB 数据遭窃取&#xff0c;波及近 9000 家教育机构、2.75 亿条用户记录&#xff0c;攻击者通过 Free‑for‑Teacher 环境工单相关漏洞获取初始…...

AI产品经理的进阶路线图

本文深入探讨了AI产品经理的核心特质&#xff0c;强调AI思维的重要性远超单纯的技术知识。文章解析了AI产品设计以极度简单为标准背后的复杂系统&#xff0c;以及AI产业链的三大结构层——基础层、技术层与应用层。此外&#xff0c;文章还详细阐述了AI产品经理的四象限分类&…...

免费CAD软件LitCAD:让你10分钟掌握专业二维绘图的终极指南

免费CAD软件LitCAD&#xff1a;让你10分钟掌握专业二维绘图的终极指南 【免费下载链接】LitCAD A very simple CAD developed by C#. 项目地址: https://gitcode.com/gh_mirrors/li/LitCAD 还在为复杂昂贵的CAD软件发愁吗&#xff1f;今天我要向你推荐一款真正简单易用的…...

kill-doc:一键下载30+文档平台的终极解决方案,告别繁琐登录验证!

kill-doc&#xff1a;一键下载30文档平台的终极解决方案&#xff0c;告别繁琐登录验证&#xff01; 【免费下载链接】kill-doc 看到经常有小伙伴们需要下载一些免费文档&#xff0c;但是相关网站浏览体验不好各种广告&#xff0c;各种登录验证&#xff0c;需要很多步骤才能下载…...

GD32F103实战指南:EXTI外部中断配置与按键响应优化

1. EXTI外部中断基础概念与GD32F103特性 外部中断&#xff08;EXTI&#xff09;是嵌入式系统中实现实时响应的关键机制。GD32F103作为Cortex-M3内核的国产MCU代表&#xff0c;其EXTI控制器具有20个独立的中断/事件线&#xff0c;支持三种触发方式&#xff1a;上升沿、下降沿以及…...