pytest_addoption 和 pytest_configure 是最轻量的本地插件方式,只需在 conftest.py 中定义即可:前者添加命令行参数(如 --env),后者初始化配置(如 config.env = config.getoption("--env")),测试中通过 request.config.env 获取,无需打包或 setuptools。

能直接通过 pytest_addoption 和 pytest_configure 注入命令行参数与配置逻辑,是最轻量、最常用的插件起步方式;不需要发包、不依赖 setuptools,conftest.py 就够用。
如何在 conftest.py 中定义基础插件行为
本地插件不是“必须打包”的概念,而是 pytest 启动时自动扫描并加载的钩子集合。只要把函数放在项目根目录或测试目录下的 conftest.py 里,且函数名符合 pytest_* 命名规范,就会被识别为插件钩子。
-
conftest.py必须位于 pytest 能推断出的“测试根目录”下(比如运行pytest tests/,则tests/conftest.py有效;但tests/sub/conftest.py不会被自动发现) - 常见钩子如
pytest_addoption(添加命令行参数)、pytest_configure(初始化配置)、pytest_runtest_makereport(修改报告内容)都可直接定义 - 不要在
conftest.py里写业务逻辑或 import 测试用例模块——它只负责插件行为,不是普通模块 - 示例:添加一个
--env=staging参数并存入 config 对象
def pytest_addoption(parser):
parser.addoption(
"--env",
action="store",
default="dev",
help="Environment to run tests against: dev/staging/prod"
)
def pytest_configure(config):
config.env = config.getoption("--env")
之后任意测试函数中可通过 request.config.env 获取该值。
为什么 pytest_plugins 变量容易失效
很多人试图在 conftest.py 中用 pytest_plugins = ["my_plugin"] 加载外部模块,却失败——根本原因是加载顺序和路径问题。
快速生成专业的 Python 脚本和应用代码。一键创建完整项目结构,支持CLI、API、爬虫、Bot、Django等多种项目类型,包含完整的项目结构、配置文件、依赖管理、测试、README和文档。
-
pytest_plugins只在当前conftest.py被加载后才生效,且要求目标模块已存在于 Python path 中(不能是相对路径或未安装的本地包) - 如果
my_plugin是个未安装的本地目录,需确保该目录有__init__.py,且已加入sys.path或通过-p显式指定 - 更可靠的做法是直接用
-p my_plugin启动:pytest -p my_plugin tests/ - 若用
PYTEST_PLUGINS环境变量,值应为逗号分隔的模块名,不是文件路径
自定义标记(@pytest.mark.xxx)需要额外注册吗
不需要。任何以 @pytest.mark. 开头的装饰器,pytest 都会原样保留到 item.keywords 中,但要让标记产生实际效果(比如跳过、过滤、修改执行逻辑),必须配合钩子函数处理。
- 例如定义
@pytest.mark.flaky(reruns=3),需在pytest_runtest_makereport或pytest_runtest_protocol中读取item.get_closest_marker("flaky") - 标记本身只是元数据,不触发任何行为;真正起作用的是你写的钩子逻辑
- 避免在标记中传复杂对象(如 lambda、类实例),只建议传基本类型(str/int/dict/list)
- 若多个插件都监听同一标记,注意执行顺序——pytest 不保证钩子调用顺序,逻辑耦合强时应合并处理
发布为 PyPI 包时 entry_points 怎么写才生效
关键不是语法对不对,而是 setuptools 是否真把你的模块识别为插件入口。常见卡点在 setup.py / pyproject.toml 配置和包结构。
- setup.py 中必须包含
entry_points={"pytest11": ["myplugin = myplugin.plugin"]},其中myplugin.plugin指向含钩子函数的模块(如myplugin/plugin.py) - 该模块不能依赖尚未安装的其他本地模块;所有 import 必须能在安装后独立 resolve
- 安装必须用
pip install -e .(开发模式)或pip install .,不能靠 PYTHONPATH 或直接拷贝 - 验证是否生效:运行
pytest --version,输出末尾应出现myplugin-0.1.0 at ...
真正难的不是写钩子,而是让钩子在不同项目、不同 Python 版本、不同 pytest 版本下稳定介入生命周期——比如 pytest_runtest_makereport 在 pytest 7+ 和 8+ 的返回结构微调过,硬编码字段名就容易崩。
Python免费学习笔记(深入):立即使用
在学习笔记中,你将探索 Python 的核心概念和高级技巧!










