Pytest Subtests实战:彻底告终断言阻塞,测试效率倍增

5 阅读5分钟

在编写自动化测试时,你是否遇到过这种“闹心”事:一个测试用例里需要校验 10 项数据,结果 第 1 项校验失败,整个用例就直接报错跳出了。剩下的 9 项到底对不对?不知道,得改完第一个再测。

以前我们要么忍受这种低效,要么写一堆复杂的 @pytest.mark.parametrize。但现在,Pytest 9.0+ 已经原生内置了 subtests 功能!它能让你在一个用例里跑多个“子测试”,互不干扰。

1. 什么是 Subtests?

subtests 是一种逻辑上的“用例分组”。它允许你在一个测试函数内部,运行多个独立的断言块。

核心特性:

  • 独立执行:某个子测试失败,后续的子测试依然会继续执行。

  • 聚合报告:在一个测试用例下汇总所有失败点。

  • 动态生成:支持在循环或逻辑分支中动态创建,比参数化更灵活。

2. 使用示例

使用 subtests 无需安装额外插件(Pytest 9.0+),直接在函数参数中引入同名 fixture 即可。

基础代码示例

假设我们要测试一个接口返回的多个字段,不希望因为 name 错了就导致没测 age

import pytestdef test_user_data_logic(subtests):    user_info = {        "name": "hogwarts",        "age": 18,        "email": "invalid-email", # 假设这是个错误数据        "status": "active"    }    for key, value in user_info.items():        # 使用 with 语句开启子测试        # msg: 自定义描述信息;后面可以跟任意数量的关键字参数(如 field=key)用于标识数据        with subtests.test(msg=f"校验字段: {key}", field=key):            if key == "email":                assert"@"in value            elif key == "age":                assert value >= 18            else:                assert value isnotNone

运行效果展示
当你运行上面的代码,你会看到类似下方的输出:

__________________ test_user_data_logic [校验字段: email] __________________subtests = <_pytest.subtests.Subtests object at 0x...>def test_user_data_logic(subtests):    ...>   assert "@" in valueE   AssertionError: assert '@' in 'invalid-email'__________________________ test_user_data_logic ___________________________contains 1 failed subtest========================= short test summary info =========================FAILED test_demo.py::test_user_data_logic - contains 1 failed subtest

即使 email 校验失败了,status 的校验依然被执行了,报告会清晰地告诉你哪个子测试(msg 或 field)出了问题。

4. 深度对比:Subtests vs 参数化

很多小伙伴会纠结:我用 @pytest.mark.parametrize 不也能达到类似效果吗?

维度

参数化 (Parametrize)

子测试 (Subtests)

定义时机

测试采集阶段

(运行前确定)

测试执行阶段

(运行时动态决定)

独立性

视为多个完全独立的用例

视为一个用例里的多个环节

性能/Setup

每个参数重新触发 setup/teardown

共享同一个 setup/teardown(更省资源)

控制力

可通过命令行 -k 筛选单个参数运行

只能整体运行,无法单独筛选子项

5. 进阶技巧

A. 控制详细输出

默认情况下,Pytest 只显示失败的子测试。如果你想在控制台看到所有(包括成功)的子测试进度,请使用 -v 参数:pytest -v test_demo.py

B. 类型提示 (Typing)

如果你追求代码规范,可以为 subtests 加上类型注解,方便 IDE 进行补全:

import pytestdef test_with_typing(subtests: pytest.Subtests):    with subtests.test():        assert True

C. 旧版本兼容

如果你还在用 Pytest 8.x 或更早的版本,只需要手动安装插件即可拥有同样功能:pip install pytest-subtests

6. 避坑指南 & 最佳实践

  1. 不要滥用:不要把整个系统的测试都塞进一个函数里。Subtests 应该用于校验同一逻辑实体的不同属性

  2. 异常处理:如果代码在 with subtests.test():之外抛出了异常(比如数据库连接失败),测试依然会立刻终止。

  3. 数据隔离:由于所有子测试共享同一个测试函数的作用域,注意不要让上一个子测试修改了下一个子测试要用的变量。

结语

subtests 的引入补全了 Pytest 在“细粒度断言”上的短板。它既保留了单个用例的简洁,又提供了多点校验的韧性。

你觉得这个功能好用吗?或者在实际项目中遇到了哪些坑?欢迎在评论区交流讨论! 🚀

关于我们

霍格沃兹测试开发学社,隶属于 测吧(北京)科技有限公司,是一个面向软件测试爱好者的技术交流社区。

学社围绕现代软件测试工程体系展开,内容涵盖软件测试入门、自动化测试、性能测试、接口测试、测试开发、全栈测试,以及人工智能测试与 AI 在测试工程中的应用实践

我们关注测试工程能力的系统化建设,包括 Python 自动化测试、Java 自动化测试、Web 与 App 自动化、持续集成与质量体系建设,同时探索 AI 驱动的测试设计、用例生成、自动化执行与质量分析方法,沉淀可复用、可落地的测试开发工程经验。

在技术社区与工程实践之外,学社还参与测试工程人才培养体系建设,面向高校提供测试实训平台与实践支持,组织开展 “火焰杯” 软件测试相关技术赛事,并探索以能力为导向的人才培养模式,包括高校学员先学习、就业后付款的实践路径。

同时,学社结合真实行业需求,为在职测试工程师与高潜学员提供名企大厂 1v1 私教服务,用于个性化能力提升与工程实践指导。