pytest不内置运行阶段顺序机制,需通过自定义mark(如@pytest.mark.stage("setup"))配合-m过滤分批执行,或使用pytest-ordering插件显式指定order;markers配置仅用于文档化,不影响执行顺序;阶段间fixture默认不共享,跨阶段数据传递应使用scope="session"并手动管理;CI中须用&&链式调用确保阶段失败即终止,并注意标记字符串匹配的准确性。

pytest标记怎么定义阶段顺序
pytest本身不内置“运行阶段”概念,@pytest.mark只是打标签,顺序得靠插件或规则驱动。最直接的方式是用pytest-ordering插件,它支持@pytest.mark.run(order=1)这类显式序号;但更通用、不依赖第三方的做法是结合@pytest.mark.stage自定义标记 + --markers过滤 + shell脚本分批调用。
- 标记本身不触发排序,只做分类:比如
@pytest.mark.stage("setup")、@pytest.mark.stage("main")、@pytest.mark.stage("teardown") - 运行时用
pytest -m "stage(setup)"单独跑某阶段,避免混跑导致状态污染 - 不要写
@pytest.mark.stage("1_setup")这种字符串排序,pytest不解析数字逻辑,纯靠字符串匹配
为什么不能只靠pytest.ini里的markers配置来控制顺序
pytest.ini里写markers = stage: test stage只是让pytest --markers能识别这个标记名,完全不影响执行顺序。很多人误以为加了这行就等于“启用阶段功能”,结果发现-m stage能过滤、但所有带stage的测试还是并行乱序执行。
- markers配置仅用于文档化和
--markers输出,无调度能力 - 若想强制顺序,必须配合外部控制(如Makefile、shell循环)或插件(如
pytest-ordering或pytest-dependency) - 注意
pytest-ordering的order值是全局比较的,不同文件里order=1会一起先跑,不是按文件分组
多阶段运行时fixture状态怎么不被污染
跨阶段复用fixture容易出问题:比如setup阶段创建的DB连接,在main阶段里拿不到,或被teardown提前清理。核心原则是——阶段间不共享fixture实例,除非显式声明scope="session"且手动管理生命周期。
- 默认
scope="function"的fixture每次test都重建,阶段切换时自动失效 - 若需跨阶段传递数据(如token、临时路径),改用
scope="session"+ 全局变量或临时文件,别依赖fixture返回值直传 - 避免在
teardown阶段的测试里调用request.getfixturevalue("xxx"),可能触发已销毁的fixture重初始化,报FixtureLookupError
实际CI中怎么安全分阶段调用pytest
CI脚本里别写pytest -m stage; pytest -m main; pytest -m teardown这种裸命令——某个阶段失败,后续仍会执行,可能造成残留状态或误判。
- 每阶段独立命令,用
&&链式执行:pytest -m stage && pytest -m main && pytest -m teardown - 给每个阶段加
--tb=short和--maxfail=1,快速暴露问题,避免浪费时间跑完全部再报错 - 如果teardown阶段必须运行(比如清理云资源),用
|| true兜底,但日志里要单独记录退出码,方便排查是否真失败
pytest多阶段本质是人为划分执行边界,没有银弹。最容易被忽略的是:标记名拼写一致性和shell命令中引号处理——-m "stage(setup)"少一个括号或空格,就会静默匹配失败,看起来像“没跑测试”。
Python免费学习笔记(深入):立即使用
在学习笔记中,你将探索 Python 的核心概念和高级技巧!











