当前位置: 首页 > 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.文学剧本台词 …...

如何在看板中体现优先级变化

在看板中有效体现优先级变化的关键措施包括&#xff1a;采用颜色或标签标识优先级、设置任务排序规则、使用独立的优先级列或泳道、结合自动化规则同步优先级变化、建立定期的优先级审查流程。其中&#xff0c;设置任务排序规则尤其重要&#xff0c;因为它让看板视觉上直观地体…...

iPhone密码忘记了办?iPhoneUnlocker,iPhone解锁工具Aiseesoft iPhone Unlocker 高级注册版​分享

平时用 iPhone 的时候&#xff0c;难免会碰到解锁的麻烦事。比如密码忘了、人脸识别 / 指纹识别突然不灵&#xff0c;或者买了二手 iPhone 却被原来的 iCloud 账号锁住&#xff0c;这时候就需要靠谱的解锁工具来帮忙了。Aiseesoft iPhone Unlocker 就是专门解决这些问题的软件&…...

dedecms 织梦自定义表单留言增加ajax验证码功能

增加ajax功能模块&#xff0c;用户不点击提交按钮&#xff0c;只要输入框失去焦点&#xff0c;就会提前提示验证码是否正确。 一&#xff0c;模板上增加验证码 <input name"vdcode"id"vdcode" placeholder"请输入验证码" type"text&quo…...

工程地质软件市场:发展现状、趋势与策略建议

一、引言 在工程建设领域&#xff0c;准确把握地质条件是确保项目顺利推进和安全运营的关键。工程地质软件作为处理、分析、模拟和展示工程地质数据的重要工具&#xff0c;正发挥着日益重要的作用。它凭借强大的数据处理能力、三维建模功能、空间分析工具和可视化展示手段&…...

cf2117E

原题链接&#xff1a;https://codeforces.com/contest/2117/problem/E 题目背景&#xff1a; 给定两个数组a,b&#xff0c;可以执行多次以下操作&#xff1a;选择 i (1 < i < n - 1)&#xff0c;并设置 或&#xff0c;也可以在执行上述操作前执行一次删除任意 和 。求…...

Java 加密常用的各种算法及其选择

在数字化时代&#xff0c;数据安全至关重要&#xff0c;Java 作为广泛应用的编程语言&#xff0c;提供了丰富的加密算法来保障数据的保密性、完整性和真实性。了解这些常用加密算法及其适用场景&#xff0c;有助于开发者在不同的业务需求中做出正确的选择。​ 一、对称加密算法…...

2025盘古石杯决赛【手机取证】

前言 第三届盘古石杯国际电子数据取证大赛决赛 最后一题没有解出来&#xff0c;实在找不到&#xff0c;希望有大佬教一下我。 还有就会议时间&#xff0c;我感觉不是图片时间&#xff0c;因为在电脑看到是其他时间用老会议系统开的会。 手机取证 1、分析鸿蒙手机检材&#x…...

PL0语法,分析器实现!

简介 PL/0 是一种简单的编程语言,通常用于教学编译原理。它的语法结构清晰,功能包括常量定义、变量声明、过程(子程序)定义以及基本的控制结构(如条件语句和循环语句)。 PL/0 语法规范 PL/0 是一种教学用的小型编程语言,由 Niklaus Wirth 设计,用于展示编译原理的核…...

NLP学习路线图(二十三):长短期记忆网络(LSTM)

在自然语言处理(NLP)领域,我们时刻面临着处理序列数据的核心挑战。无论是理解句子的结构、分析文本的情感,还是实现语言的翻译,都需要模型能够捕捉词语之间依时序产生的复杂依赖关系。传统的神经网络结构在处理这种序列依赖时显得力不从心,而循环神经网络(RNN) 曾被视为…...

在WSL2的Ubuntu镜像中安装Docker

Docker官网链接: https://docs.docker.com/engine/install/ubuntu/ 1、运行以下命令卸载所有冲突的软件包&#xff1a; for pkg in docker.io docker-doc docker-compose docker-compose-v2 podman-docker containerd runc; do sudo apt-get remove $pkg; done2、设置Docker…...