自定义 pytest 标记需在 pyproject.toml 或 pytest.ini 中注册,否则会被静默忽略;支持多标记组合筛选(如 -m "smoke and integration");带参标记通过 get_closest_marker().args/kwargs 读取;多进程下仅 function 级 fixture 安全读取标记。

为什么直接用 @pytest.mark.xxx 会失效
常见现象是加了 @pytest.mark.smoke,但运行 pytest -m smoke 没有匹配到任何用例。根本原因不是标记写错了,而是 pytest 默认只识别预定义的标记名(如 slow、unit),自定义标记必须显式注册,否则会被忽略——哪怕语法完全正确,也会静默跳过。
解决方法是在项目根目录下创建 pyproject.toml 或 pytest.ini,声明标记名:
[tool:pytest]
markers =
smoke: end-to-end smoke test
integration: calls external services
flaky: known intermittent failure
注意:文件必须在 pytest 启动时能被找到(通常放在与 test_*.py 同级或更上层目录);如果用 pyproject.toml,section 名要写成 [tool.pytest.ini_options],且 markers 值为数组格式。
如何按组合条件运行带多个 @pytest.mark 的用例
一个测试函数可以叠加多个标记,比如:
@pytest.mark.smoke
@pytest.mark.integration
def test_login_flow():
...
此时它既属于 smoke 集合,也属于 integration 集合。运行时可用逻辑表达式筛选:
-
pytest -m "smoke and integration"—— 同时带两个标记 -
pytest -m "smoke or slow"—— 带任一标记 -
pytest -m "not flaky"—— 排除 flaky 标记
注意:表达式中不能有空格(如 "smoke and integration" 必须加引号),否则 shell 会拆分;and/or/not 优先级固定,不支持括号分组;字符串值(如 @pytest.mark.env("staging"))无法参与布尔运算,只能用 -k 模糊匹配。
怎么给 @pytest.mark 传参并动态读取
标记本身可带参数,常用于环境、数据集、版本等上下文信息:
@pytest.mark.env("staging")
@pytest.mark.version("v2.1")
def test_api_compatibility():
...
在 fixture 或钩子中读取这些参数,需要访问测试节点的 get_closest_marker():
def test_with_marker(request):
env_marker = request.node.get_closest_marker("env")
if env_marker:
env = env_marker.args[0] # "staging"
assert env in ["staging", "prod"]
关键点:
-
.args是元组,按标记调用时的顺序取值(@pytest.mark.env("a", "b")→args == ("a", "b")) -
.kwargs对应命名参数(@pytest.mark.env(name="staging")→kwargs == {"name": "staging"}) - 未注册的带参标记不会报错,但
get_closest_marker()返回None,务必判空
标记和 pytest 插件(如 pytest-xdist)的兼容性问题
多进程执行时(pytest -n 4),标记本身不受影响,但如果你在 conftest.py 中用标记控制 fixture 行为,要注意作用域:
- session-scoped fixture 里读取标记 → 不安全,因为标记属于单个测试节点,session 级别看不到
- function-scoped fixture 里读取 → 安全,每个测试独立执行,
request.node可用 - 用
pytest_configure钩子预扫描所有标记 → 可行,但需手动解析config.pluginmanager,复杂度高,一般没必要
真正容易踩坑的是标记 + --lf(last-failed):如果上次失败的用例被新标记过滤掉,--lf 会静默跳过,而不是报错提示“无匹配用例”。建议在 CI 脚本中加一层检查:pytest --collect-only -m "your_mark" | grep "collected 0"。
Python免费学习笔记(深入):立即使用
在学习笔记中,你将探索 Python 的核心概念和高级技巧!











