如何从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_path

    if 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 == 2

def test_subtraction():
    assert 2 - 1 == 1

def 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_packages

setup(
    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工具的实现过程以及原理。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mfbz.cn/a/757589.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

全网唯一免费无水印AI视频工具!

最近Morph Studio开始免费公测!支持高清画质,可以上传语音,同步口型,最重要的是生成的视频没有水印! Morph Studio国内就可以访问,可以使用国内邮箱注册(我用的163邮箱),…

基于协同过滤的电影推荐与大数据分析的可视化系统

基于协同过滤的电影推荐与大数据分析的可视化系统 在大数据时代,数据分析和可视化是从大量数据中提取有价值信息的关键步骤。本文将介绍如何使用Python进行数据爬取,Hive进行数据分析,ECharts进行数据可视化,以及基于协同过滤算法…

【FFmpeg】avformat_write_header函数

FFmpeg相关记录: 示例工程: 【FFmpeg】调用ffmpeg库实现264软编 【FFmpeg】调用ffmpeg库实现264软解 【FFmpeg】调用ffmpeg库进行RTMP推流和拉流 【FFmpeg】调用ffmpeg库进行SDL2解码后渲染 流程分析: 【FFmpeg】编码链路上主要函数的简单分…

LeetCode 207. 课程表

思路:这是一道拓扑排序问题,拓扑排序听起来可能有点复杂,但实际上它是个相当直观的概念。想象一下,你有很多事情要做,但有些事情必须在另一些事情完成之后才能开始,就像你得先穿上袜子再穿鞋子 拓扑排序就…

【UML用户指南】-23-对高级行为建模-状态机

目录 1、概述 2、状态 2.1、状态的组成 3、转移 3.1、转移的组成 4、高级状态和转移 4.1、进入效应和退出效应 4.2、内部转移 4.3、do活动 4.4、延迟事件 4.5、子状态机 5、子状态 5.1、非正交子状态 5.2、历史状态 5.3、正交子状态 6、分叉与汇合 7、主动对象…

Uboot重定位

Uboot重定位 一、重定位的意义二、介绍一些重定位相关的表项结构(节)三、uboot的重定位过程:一、重定位的意义 uboot的重定位有两次,第一次是在编译成镜像后,在makefile中调用进行处理的,其调用tools/riscv_prelink.c的代码进行重定位处理(主要就是对重定位表中的R_RIS…

Linux多进程和多线程(一)

进程 进程的概念 进程(Process)是操作系统对一个正在运行的程序的一种抽象。它是系统运行程序的最小单位,是资源分配和调度的基本单位。 进程的特点如下 进程是⼀个独⽴的可调度的活动, 由操作系统进⾏统⼀调度, 相应的任务会被调度到cpu …

[软件安装]Dev C++

一、下载Dev C软件安装包 1、官网下载官网 2、百度网盘下载压缩包 二、安装Dev C 1、解压Dev C软件安装包 2、找到【Dev-Cpp 5.11…】应用程序,右键选择【以管理员身份运行】它 3、设置语言 回到桌面,右键桌面上的【Dev C 5.11软件图标】&#xff0c…

vue插槽的简单使用

默认插槽 1.在Category中创建插槽 <slot>默认值<slot/> 2.在App中使用 <Category tittle"美食"> <ul ><li v-for"(l,index) in foods" :key"index">{{l}}</li></ul> </Category> 3.运行后的…

用英文介绍美国总统:Barack Obama First African-American President (2009 – 2017)

Barack Obama: First African-American President (2009 – 2017) Link: https://www.youtube.com/watch?vwHCBI3yypmE&listPLybg94GvOJ9E-ZM1U6PAjgPUmz-V4-Yja&index44 Introduction Barack Obama made history as the first African-American elected to the pre…

中国电信股份有限公司江西分公司招聘信息 7.5日截止

法律事务管理(南昌) 学历要求 本科及以上学历 岗位职责 1.依据国家法律、法规和相关规章规定,为公司其他部门提供日常法律服务与支持; 2.负责公司各类合同审核工作; 3.负责公司法律文件的起草和法律事务谈判; 4.围绕与公司业务有关的法律问题及法…

Alibaba Cloud Linux详解_操作系统兼容性_alinux稳定性全解析

Alibaba Cloud Linux是阿里云自研的稳定、安全、高性能的服务器Linux操作系统&#xff0c;完全兼容CentOS/RHEL生态和操作方式&#xff0c;又阿里云提供免费提供长期支持和维护LTS。Alibaba Cloud Linux是目前阿里云服务器最大规模使用的操作系统之一&#xff0c;可部署在Web网…

基于GWO灰狼优化的多目标优化算法matlab仿真

目录 1.程序功能描述 2.测试软件版本以及运行结果展示 3.核心程序 4.本算法原理 4.1灰狼优化算法原理 4.2 多目标优化问题(MOP)的帕累托最优解 4.3 基于GWO的多目标优化算法 5.完整程序 1.程序功能描述 基于GWO灰狼优化的多目标优化算法matlab仿真&#xff0c;目标函数…

华为数通——STP-RSTP-MSTP生成树

STP 为了提高网络可靠性&#xff0c;交换机之间常常会进行设备冗余&#xff08;备份&#xff09;&#xff0c;但这样会给交换网络带来环路风险&#xff0c;导致广播风暴以及MAC地址表不稳定等问题。 STP&#xff1a;生成树协议的作用就是为了解决避免二层环路&#xff0c;解决…

[NeurIPS2021] Deep Residual Learning in Spiking Neural Networks【文献精读、翻译】

深度残差学习在脉冲神经网络中的应用 Fang W, Yu Z, Chen Y, et al. Deep residual learning in spiking neural networks[J]. Advances in Neural Information Processing Systems, 2021, 34: 21056-21069. 摘要 深度脉冲神经网络 (SNNs) 因为使用离散的二进制激活和复杂的时…

FlowUs息流打造AI赋能下的知识库,信息深度挖掘与智能创作!FlowUs让你的数据资产更有价值

在AI时代的大潮中&#xff0c;FlowUs息流笔记类数据库凭借其强大的数据资产管理能力&#xff0c;正以前所未有的方式重塑着知识工作者的学习、研究与协作模式。当深厚的数据资产遇上AI的智能助力&#xff0c;无论是学术论文的撰写&#xff0c;还是高效提炼多人会议的核心观点&a…

web前端大作业-乡村扶贫、乡村振兴

文章目录 代码分析页面截图代码连接 代码分析 代码结构 主页index.html <!DOCTYPE html> <html lang"en"><head><meta charset"UTF-8"><meta http-equiv"X-UA-Compatible" content"IEedge"><meta…

STC89C52RC单片机设计的FM收音机+自动搜台+存储电台(程序+原理图+PCB)

资料下载地址&#xff1a;STC89C52RC单片机设计的FM收音机自动搜台存储电台&#xff08;程序原理图PCB) 1、实物图 2、部分程序 #include <reg52.h> #include "tea5767.h" #include "delay.h" #include "lcd1602.h" //K1:上一台 K2:下一…

Redis基础教程(二):redis数据类型

&#x1f49d;&#x1f49d;&#x1f49d;首先&#xff0c;欢迎各位来到我的博客&#xff0c;很高兴能够在这里和您见面&#xff01;希望您在这里不仅可以有所收获&#xff0c;同时也能感受到一份轻松欢乐的氛围&#xff0c;祝你生活愉快&#xff01; &#x1f49d;&#x1f49…

电商平台数据爬取经验分享

一、引言 在电商领域&#xff0c;数据的重要性不言而喻。无论是市场趋势分析、竞争对手研究&#xff0c;还是用户行为洞察&#xff0c;都离不开数据的支持。而数据爬虫作为获取这些数据的重要工具&#xff0c;其技术的掌握和运用对于电商平台来说至关重要。本文将结合个人实际…