[测试技术] Pytest 入门与实战:fixture、参数化、标记与并行执行
原创内容未获授权禁止转载、转发、抄袭。Python 自动化测试从几个脚本增长到完整测试集后通常会遇到数据重复准备、场景难以筛选、失败信息不直观和执行时间变长等问题。pytest 使用普通assert编写断言并通过 fixture、参数化、标记和插件机制组织测试适合单元测试、接口测试和回归测试。本文使用一个小型完整的内存“取消订单”案例演示 pytest 的核心用法并实际验证全量、冒烟和并行三种执行方式。pytest 适合解决什么问题能力典型用途断言重写使用普通assert失败时展示表达式中的实际值fixture准备测试数据、注入依赖并清理资源参数化用多组输入覆盖同一业务规则mark区分冒烟、回归、慢测试等测试集合插件扩展并行、报告、覆盖率等能力目前PyPI 上 pytest 最新稳定版为9.1.1发布于 2026 年 6 月 19 日要求 Python3.10。并行执行不是 pytest 内置能力本文使用 pytest-xdist3.8.0该插件要求 Python3.9因此本例最终以 pytest 的3.10为准。环境与项目结构示例目录如下pytest-demo/ ├── order_service.py ├── pytest.ini ├── requirements-test.txt └── tests/ ├── conftest.py ├── test_order_service.py └── test_order_status.py创建requirements-test.txt固定两个直接测试依赖的版本pytest9.1.1 pytest-xdist3.8.0macOS 和 Linux 创建并激活虚拟环境python3-mvenv .venvsource.venv/bin/activate python--versionpython-mpipinstall-rrequirements-test.txtWindows PowerShell 使用py-m venv.venv.\.venv\Scripts\Activate.ps1 python--version python-m pip install-r requirements-test.txt安装前应确认输出为 Python3.10。本文统一从项目根目录使用python -m pytest。与直接调用pytest命令相比这种方式会把当前目录加入sys.path示例中的order_service.py才能被稳定导入。创建pytest.ini[pytest] minversion 9.1 testpaths tests addopts -ra --strict-config --strict-markers markers smoke: 核心链路冒烟测试 regression: 完整回归测试testpaths限定测试目录避免在大型仓库中误收集脚本--strict-config和--strict-markers会让未知配置项、未注册或拼错的自定义标记直接报错。默认测试文件使用test_*.py或*_test.py函数使用test_*测试类使用Test*且不能定义__init__。准备被测代码本例包含以下规则只有待支付订单可以取消商品数量必须大于 0取消成功后订单状态变为已取消并释放对应库存已支付或不存在的订单取消失败且不能释放库存已取消订单再次取消失败且不能重复释放库存创建order_service.pyfromdataclassesimportdataclass,replacefromenumimportEnumclassOrderNotFoundError(LookupError):passclassOrderStatus(Enum):PENDINGpendingPAIDpaidCANCELLEDcancelleddefcan_cancel(self)-bool:returnselfisOrderStatus.PENDINGdataclass(frozenTrue)classOrder:order_id:intquantity:intstatus:OrderStatusdef__post_init__(self)-None:ifself.quantity0:raiseValueError(商品数量必须大于0)classOrderRepository:def__init__(self)-None:self._orders:dict[int,Order]{}defsave(self,order:Order)-None:self._orders[order.order_id]orderdeffind(self,order_id:int)-Order|None:returnself._orders.get(order_id)classOrderService:def__init__(self,repository:OrderRepository)-None:self._repositoryrepository self._released_quantity0propertydefreleased_quantity(self)-int:returnself._released_quantitydefadd(self,order:Order)-None:self._repository.save(order)defcancel(self,order_id:int)-Order:orderself._repository.find(order_id)iforderisNone:raiseOrderNotFoundError(订单不存在)ifnotorder.status.can_cancel():raiseValueError(当前状态不允许取消)cancelledreplace(order,statusOrderStatus.CANCELLED)self._repository.save(cancelled)self._released_quantityorder.quantityreturncancelledOrderRepository是本例可观察的存储边界测试可以验证取消失败后订单没有被错误修改。正式项目更适合使用src目录布局并安装项目包真实取消流程如果同时修改数据库订单和外部库存还需要事务或幂等补偿不能用这个内存示例推导生产一致性。fixture统一准备测试对象创建tests/conftest.pyimportpytestfromorder_serviceimportOrderRepository,OrderServicepytest.fixturedeforder_repository()-OrderRepository:returnOrderRepository()pytest.fixturedeforder_service(order_repository:OrderRepository)-OrderService:returnOrderService(order_repository)测试函数只要声明同名参数pytest 就会自动注入 fixture不需要导入conftest.py。order_service依赖order_repository同一条测试即使同时声明这两个参数也会复用同一个仓库实例。fixture 默认作用域为function每条用例都会创建独立仓库测试结束后对象直接丢弃不需要额外清理。数据库连接、临时服务等外部资源应使用yield并在其后执行关闭操作测试函数失败时清理代码仍会执行但 fixture 在到达yield前失败时不会执行后半段。conftest.py中的 fixture 对其所在目录及子目录生效重名 fixture 可能被下层目录覆盖。可用作用域包括function、class、module、package和session。扩大作用域能减少初始化次数但也会增加状态污染风险不应只为提速就共享可变对象。断言正常与异常路径创建tests/test_order_service.pyimportpytestfromorder_serviceimport(Order,OrderNotFoundError,OrderRepository,OrderService,OrderStatus,)pytest.mark.regressionpytest.mark.parametrize(quantity,[pytest.param(0,idzero),pytest.param(-1,idnegative),],)deftest_order_quantity_must_be_positive(quantity:int)-None:withpytest.raises(ValueError,match商品数量必须大于0):Order(1000,quantity,OrderStatus.PENDING)pytest.mark.smokepytest.mark.regressiondeftest_cancel_pending_order_releases_inventory(order_service:OrderService,order_repository:OrderRepository,)-None:order_service.add(Order(1001,2,OrderStatus.PENDING))actualorder_service.cancel(1001)assertactual.order_id1001assertactual.statusisOrderStatus.CANCELLED storedorder_repository.find(1001)assertstoredisnotNoneassertstored.statusisOrderStatus.CANCELLEDassertorder_service.released_quantity2pytest.mark.regressiondeftest_paid_order_does_not_release_inventory(order_service:OrderService,order_repository:OrderRepository,)-None:order_service.add(Order(1002,1,OrderStatus.PAID))withpytest.raises(ValueError,match当前状态不允许取消):order_service.cancel(1002)storedorder_repository.find(1002)assertstoredisnotNoneassertstored.statusisOrderStatus.PAIDassertorder_service.released_quantity0pytest.mark.regressiondeftest_repeated_cancel_does_not_release_inventory_twice(order_service:OrderService,order_repository:OrderRepository,)-None:order_service.add(Order(1003,2,OrderStatus.PENDING))order_service.cancel(1003)withpytest.raises(ValueError,match当前状态不允许取消):order_service.cancel(1003)storedorder_repository.find(1003)assertstoredisnotNoneassertstored.statusisOrderStatus.CANCELLEDassertorder_service.released_quantity2pytest.mark.regressiondeftest_missing_order_does_not_release_inventory(order_service:OrderService,)-None:withpytest.raises(OrderNotFoundError)asexc_info:order_service.cancel(9999)asserttype(exc_info.value)isOrderNotFoundErrorassertorder_service.released_quantity0pytest 会重写测试模块中的assert失败时展示参与比较的值不需要额外断言类。pytest.raises()会接受目标异常的子类要求类型完全一致时可像不存在订单的用例一样断言type(exc_info.value)。match按正则表达式匹配异常文本只应在消息属于接口或产品契约时使用如果消息中包含[]、()等正则字符可使用re.escape()。正常用例同时检查返回对象、仓库状态和库存释放量防止实现只返回新对象却漏掉持久化。异常用例则读取仓库确认状态未被修改并检查released_quantity防止失败路径错误释放库存。参数化覆盖状态规则创建tests/test_order_status.pyimportpytestfromorder_serviceimportOrderStatuspytest.mark.regressionpytest.mark.parametrize((status,expected),[pytest.param(OrderStatus.PENDING,True,idpending),pytest.param(OrderStatus.PAID,False,idpaid),pytest.param(OrderStatus.CANCELLED,False,idcancelled),],)deftest_only_pending_status_can_be_cancelled(status:OrderStatus,expected:bool,)-None:assertstatus.can_cancel()isexpected每个参数组合都会成为独立测试id会出现在收集结果和失败报告中比默认对象表示更容易定位。测试数据应直接给出期望值不要在参数生成逻辑中复制生产代码的判断条件。pytest 会把参数对象原样传给测试不会自动复制列表、字典等可变对象。如果测试会修改参数应在用例内复制或改用 fixture 创建避免前一个参数用例影响后一个。当参数只表示输入和期望值时使用pytest.mark.parametrize当每组参数还需要创建并清理资源时使用带params的参数化 fixture 更合适。标记与测试筛选本例注册了smoke和regression两个标记可以按测试范围执行同一条测试可以拥有多个 mark。本例的正常取消既属于冒烟范围也属于完整回归范围因此同时标记为smoke和regression。# 执行全部测试python-mpytest# 只执行冒烟测试python-mpytest-msmoke# 执行回归但排除冒烟测试python-mpytest-mregression and not smoke# 按名称表达式筛选python-mpytest-kcancel and not missing-m匹配 mark-k匹配测试节点名称、父级名称和关键字两者可以组合使用最终只执行同时满足两个表达式的测试。长期稳定的测试类型适合使用 mark临时定位某个模块或函数时使用-k。使用 pytest-xdist 并行执行安装 pytest-xdist 后通过-n指定 worker 数# 使用两个进程并行执行python-mpytest-n2# 根据可用 CPU 自动选择进程数python-mpytest-nauto# 同一模块的测试分配给同一 workerpython-mpytest-n2--distloadscopepytest-xdist 使用多个 worker 进程分发测试。每个进程拥有独立内存但账号、数据库、文件和远程环境仍可能冲突测试数据需要按 worker 或用例隔离。测试可注入 xdist 提供的worker_idfixture使用gw0、gw1等值生成不同的数据前缀。session作用域 fixture 也是每个 worker 各执行一次必须全局只执行一次的初始化应放在测试运行前或通过文件锁、数据库锁等跨进程机制保护。--dist loadscope会把同一模块的函数或同一测试类的方法交给同一 worker有利于复用模块、类级 fixture但负载可能不如默认调度均匀。无论使用哪种策略都不应依赖收集顺序或完成顺序。并行存在进程启动和调度开销短小测试可能比串行更慢。先在 CI 中记录耗时和失败率再决定 worker 数依赖执行顺序或共享全局状态的测试应先拆除依赖再开启并行。执行与报告本文示例在 Python3.12.13、pytest9.1.1、pytest-xdist3.8.0环境中实际执行执行方式结果python -m pytest -q9 条通过python -m pytest -q -m smoke1 条通过8 条未选择python -m pytest -q -n 29 条通过CI 中可生成 JUnit XML由流水线归档python-mpytest--durations10--junitxmlreports/pytest.xml--durations10会列出最慢的 10 个 setup、call 或 teardown 阶段适合判断慢点来自测试步骤还是 fixture。失败排查时可使用-x在首次失败后停止或使用--maxfail3限制失败数修复后用--lf只执行上次失败的用例。不要在团队默认配置中长期启用--pdb否则无人值守的 CI 会停在交互调试状态。--lf依赖项目根目录下的.pytest_cache。虚拟环境、.pytest_cache、__pycache__和本地reports目录应加入.gitignoreCI 报告则通过流水线制品归档。常见问题问题处理方式没有收集到测试检查文件、函数命名和testpaths配置CI 提示 no tests ranpytest 未收集到测试时退出码为 5应修正收集规则不要直接忽略失败fixture not found检查 fixture 名称及conftest.py所在目录Unknown mark在配置文件中注册 mark并开启--strict-markers参数化用例名称难懂使用pytest.param(..., id...)设置业务化名称单独执行通过并行失败排查共享账号、文件、数据库记录和端口冲突session fixture 初始化多次xdist 下每个 worker 都有独立测试会话本地能导入CI 导入失败固定从项目根目录执行并使用虚拟环境或正式安装项目包工程实践建议一个 fixture 只负责一种资源的准备与清理不在其中隐藏核心测试步骤每条测试独立创建数据并能脱离其他用例单独执行参数化只承载同一规则的多组输入不把不同业务流程塞进一张数据表对异常路径同时验证异常、状态和不应发生的副作用在配置中注册 mark、固定插件版本并让本地与 CI 使用同一命令并行执行前先解决数据隔离再根据 CI 实测结果调整 worker 数总结pytest 的核心是让测试依赖、数据和执行范围保持清晰用 fixture 管理上下文用参数化覆盖规则用 mark 选择测试集并在用例能够独立执行、数据已经隔离后再使用 xdist 并行。

相关新闻

【愚公系列】《Android应用案例开发大全》002-Android 开发环境的搭建

【愚公系列】《Android应用案例开发大全》002-Android 开发环境的搭建

💎【行业认证权威头衔】 ✔ 华为云天团核心成员:特约编辑/云享专家/开发者专家/产品云测专家 ✔ 开发者社区全满贯:CSDN博客&商业化双料专家/阿里云签约作者/腾讯云内容共创官/掘金&亚马逊&51CTO顶级博主 ✔ 技术生态共建先锋&am…

2026/7/23 21:09:28阅读更多 →
北京华恒智信助力国有温泉酒店破解人才选拔无标准难题

北京华恒智信助力国有温泉酒店破解人才选拔无标准难题

【客户行业】国有企业;旅游行业;温泉度假【问题类型】人才盘点【客户背景】某温泉酒店是国有企业下属公司,该温泉酒店有室内室外温泉池共30余个,除温泉外,该酒店还有各种类型套房,包括:豪华套房…

2026/7/23 21:09:28阅读更多 →
OMAP5912核心外设实战:USB、摄像头、串口与DMA驱动开发详解

OMAP5912核心外设实战:USB、摄像头、串口与DMA驱动开发详解

1. 项目概述:从芯片手册到实战,拆解OMAP5912核心外设如果你手头有一块基于TI OMAP5912双核处理器的老设备,比如一些早期的PDA、工业手持终端或者多媒体网关,想要为它开发驱动或者进行系统级优化,那么芯片手册里那几百页…

2026/7/23 21:07:28阅读更多 →
语音交互与LLM结合:技术原理与实战应用指南

语音交互与LLM结合:技术原理与实战应用指南

语音交互成LLM最佳输入方式:技术原理与实战应用指南在人工智能技术快速发展的今天,大型语言模型(LLM)已成为各行各业的热门工具。然而,传统的文本输入方式存在效率瓶颈,特别是在移动场景、无障碍交互和实时…

2026/7/24 2:26:33阅读更多 →
Codex自定义代码审查规则:从配置到CI/CD集成的完整指南

Codex自定义代码审查规则:从配置到CI/CD集成的完整指南

Codex 最近推出了自定义代码审查规则功能,这个更新让开发者能够根据团队规范或项目需求,灵活定制代码审查逻辑。无论是检查代码风格、安全漏洞,还是业务逻辑合规性,现在都可以通过规则引擎快速配置。如果你正在寻找更智能、可定制…

2026/7/24 2:26:33阅读更多 →
C++游戏模拟器工程实践:从面向对象设计到数据驱动架构

C++游戏模拟器工程实践:从面向对象设计到数据驱动架构

1. 项目概述:从“魔兽世界”到C大作业的工程化思考 看到“魔兽世界 C 大作业完整代码”这个标题,很多同学可能会眼睛一亮,觉得找到了一个既有趣又能拿高分的捷径。但作为一个带过不少学生项目、自己也写过游戏模拟器的老码农,我得…

2026/7/24 2:26:33阅读更多 →
PPL-Factory:任务与预算双感知的AI训练数据选择框架

PPL-Factory:任务与预算双感知的AI训练数据选择框架

在AI模型训练中,数据选择往往是最容易被忽视却至关重要的环节。传统方法要么简单随机采样,要么依赖人工经验筛选,但面对动辄TB级的预训练数据和复杂的下游任务,这些方法要么效率低下,要么效果不可控。PPL-Factory的出现…

2026/7/24 2:26:33阅读更多 →
ChatGPT邮件日历插件:智能办公自动化配置与实战指南

ChatGPT邮件日历插件:智能办公自动化配置与实战指南

这次我们来看一个能显著提升工作效率的 ChatGPT 插件方案——通过插件连接邮件和日历系统。这个方案的核心价值在于,它能将 ChatGPT 的对话能力与你日常的邮件处理和日程安排无缝衔接,让你在一个界面内完成多项任务。对于经常需要处理邮件、管理日程的职…

2026/7/24 2:26:33阅读更多 →
Qwen-VL多模态大模型:部署、微调与应用实践

Qwen-VL多模态大模型:部署、微调与应用实践

1. Qwen-VL模型概述Qwen-VL(通义千问视觉语言大模型)是阿里巴巴云推出的多模态大模型系列,基于Qwen(通义千问)语言大模型扩展而来。这个系列模型能够同时处理图像、文本和边界框输入,输出文本和边界框标注&…

2026/7/24 2:24:32阅读更多 →
Go语言静态资源打包方案对比与实践指南

Go语言静态资源打包方案对比与实践指南

1. 项目背景与核心需求在Go语言开发中,我们经常需要处理静态资源文件的打包问题。无论是Web应用的模板文件、前端资源,还是配置文件、证书等,都需要随程序一起分发。传统做法是将这些文件与编译后的二进制文件放在同一目录下,但这…

2026/7/24 0:58:53阅读更多 →
Go语言实现高性能LDAP认证服务的架构与实践

Go语言实现高性能LDAP认证服务的架构与实践

1. 项目背景与核心价值LDAP(轻量级目录访问协议)作为企业级身份认证的黄金标准,已经服务了超过80%的财富500强公司。我在金融科技领域实施统一认证体系时,发现传统Java方案存在启动慢、内存占用高等痛点。而Go语言凭借其协程并发模…

2026/7/24 0:58:53阅读更多 →
【AI面试官实战指南】:用ChatGPT模拟10类高频技术岗面试,3天提升应答精准度92%

【AI面试官实战指南】:用ChatGPT模拟10类高频技术岗面试,3天提升应答精准度92%

更多请点击: https://intelliparadigm.com 第一章:AI面试官实战指南的核心价值与适用场景 AI面试官并非替代人类HR的“黑箱工具”,而是以可解释、可审计、可迭代的方式,赋能招聘全链路的关键基础设施。其核心价值在于将主观经验沉…

2026/7/24 0:58:53阅读更多 →
我的编程之路:第一篇博客

我的编程之路:第一篇博客

大家好,我是一名编程初学者,同时这也是我编程学习之路上的第一篇博客。在这里,我想要向大家介绍我的一些想法和规划。a.自我介绍我是一个刚刚接触编程的新手,目前在学习c语言,我对编程世界充满了强烈的好奇。当然&…

2026/7/24 0:00:06阅读更多 →
【LeetCode 54】螺旋矩阵

【LeetCode 54】螺旋矩阵

问题描述: 解法: 1、模拟(参考自【LeetCode 54】螺旋矩阵-CSDN博客) int *spiralOrder(int **matrix, int matrixSize, int *matrixColSize, int *returnSize) {static const int dirs[4][2] {{0, 1}, {1, 0}, {0, -1}, {-1, …

2026/7/24 0:00:06阅读更多 →
2026 WAIC:模型隐身、智能体疯野,厂商竞赛聚焦办公场景与商业闭环

2026 WAIC:模型隐身、智能体疯野,厂商竞赛聚焦办公场景与商业闭环

知春路不相信模型领先今年WAIC大会,昔日AI六小龙来了五家,分别是Kimi、阶跃星辰、Minimax、百川智能、零一万物。连放弃基模的百川和零一万物都来了,唯一缺席的竟是近几个月来风光无限的智谱。(DeepSeek一直不参加)WAI…

2026/7/24 0:00:06阅读更多 →
YOLOv8推理性能优化:从1.2FPS到35FPS的全链路加速实践

YOLOv8推理性能优化:从1.2FPS到35FPS的全链路加速实践

如果你在部署 YOLOv8 时,发现推理速度只有可怜的 1-2 FPS,而别人的演示视频却能跑到 30 FPS 以上,那么问题很可能不在模型本身,而在于你的整个处理链路。很多开发者拿到一个训练好的 YOLOv8 模型后,会直接使用官方示例…

2026/7/23 22:58:43阅读更多 →
Coze与Dify对比指南:低代码AI应用开发从入门到实战

Coze与Dify对比指南:低代码AI应用开发从入门到实战

1. 从零到一:为什么你需要了解 Coze 和 Dify?如果你对 AI 应用开发感兴趣,但一看到“大模型”、“智能体”、“工作流”这些词就头疼,觉得门槛太高,那这篇文章就是为你准备的。很多开发者,包括我自己&#…

2026/7/23 18:58:18阅读更多 →
AI生图工具怎么选?2026年6月版实测对比

AI生图工具怎么选?2026年6月版实测对比

做自媒体的朋友应该都有体会:配图一直是个让人头疼的问题。2026年,AI生图工具已经非常成熟了,但工具太多反而不知道怎么选。以下是截至2026年6月我对主流AI生图工具的实测对比。Midjourney V8.1:速度之王2026年6月11日&#xff0c…

2026/7/23 18:58:18阅读更多 →