将Python第三方库转换为真正的 pytest 插件

打印 上一主题 下一主题

主题 1930|帖子 1930|积分 5790

马上注册,结交更多好友,享用更多功能,让你轻松玩转社区。

您需要 登录 才可以下载或查看,没有账号?立即注册

x
题目配景

在使用 pytest 进行测试时,我遇到了这样的错误:
  1. Defining 'pytest_plugins' in a non-top-level conftest is no longer supported: It affects the entire test suite instead of just below the conftest as expected.
复制代码
这个错误通常出现在测试工程的结构中有多层 conftest.py 文件,并且在非顶层的 conftest 中定义了 pytest_plugins。从 pytest 7.0.0 版本开始,这种用法被废弃,因为它会影响整个测试套件而不仅仅是该 conftest.py 以下的测试。
案例中,测试工程根目录下有一个 conftest.py,此中包含:
  1. pytest_plugins = ["my_python_lib.base.testbase.conftest"]
复制代码
这里 my_python_lib 是一个自定义的 Python 第三方库,测试工程中的用例必要调用 my_python_lib.base.testbase.conftest 中的 fixture。
最佳办理方案:将库转换为真正的 pytest 插件

将我们的库转换为一个真正的 pytest 插件是最优雅和最可维护的办理方案。这样不仅办理了当前题目,还提高了代码的可复用性和可扩展性。
步调 1:重构库结构

起首,调整库结构,确保 fixture 代码位于符合的模块中:
  1. my_python_lib/
  2. ├── __init__.py
  3. ├── base/
  4. │   ├── __init__.py
  5. │   ├── testbase/
  6. │   │   ├── __init__.py
  7. │   │   ├── fixture.py  # 将 conftest.py 中的 fixture 移到这里
  8. │   │   └── plugin.py   # 新建的插件入口点文件
复制代码
步调 2:创建插件入口点文件

创建 plugin.py 文件,导入全部 fixture 并定义任何必要的 pytest 钩子:
  1. # my_python_lib/base/testbase/plugin.py
  2. from .fixture import *  # 导入所有的 fixture
  3. # 在这里可以定义 pytest 钩子函数
  4. def pytest_configure(config):
  5.     """
  6.     pytest 配置阶段被调用的钩子
  7.     可以在这里进行全局配置
  8.     """
  9.     pass
复制代码
步调 3:修改库的 setup.py

在库的 setup.py 中添加 pytest 插件的入口点:
  1. from setuptools import setup, find_packages
  2. setup(
  3.     name="my_python_lib",
  4.     version="1.0.0",
  5.     packages=find_packages(),
  6.     description="我的测试工具库",
  7.     author="Your Name",
  8.     author_email="your.email@example.com",
  9.    
  10.     # 添加 pytest 插件入口点,这里的 pytest11 是一个固定写法,了解到这个情况的我,感觉这简直“逆天”
  11.     entry_points={
  12.         'pytest11': [
  13.             'my_lib = my_python_lib.base.testbase.plugin',
  14.         ],
  15.     },
  16.     # 添加依赖
  17.     install_requires=[
  18.         'pytest>=6.0.0',
  19.         # 其他依赖...
  20.     ],
  21. )
复制代码
步调 4:重新安装库

  1. pip uninstall -y my_python_lib  # 先卸载当前版本
  2. cd /path/to/my_python_lib
  3. pip install -e .  # 以开发模式安装
复制代码
步调 5:修改测试项目

删除测试项目中 conftest.py 中的 pytest_plugins 定义,因为现在插件会自动加载:
  1. # 测试项目的 conftest.py# 删除这一行:# pytest_plugins = ["my_python_lib.base.testbase.conftest"]
  2. # 可以添加其他测试项目特定的 fixturedef pytest_configure(config):    # 测试项目特定的配置    pass
复制代码
步调 6:验证插件是否精确安装

运行以下命令验证插件是否被精确识别:
  1. python -m pytest --trace-config
复制代码
应该能看到雷同这样的输出:
  1. pytest11 plugin registration SETUP: my_python_lib.base.testbase.plugin
复制代码
代码示例

fixture.py 示例

  1. # my_python_lib/base/testbase/fixture.py
  2. import pytest
  3. import os
  4. import tempfile
  5. @pytest.fixture
  6. def temp_dir():
  7.     """提供一个临时目录"""
  8.     with tempfile.TemporaryDirectory() as temp_dir:
  9.         yield temp_dir
  10. @pytest.fixture
  11. def temp_file():
  12.     """提供一个临时文件"""
  13.     with tempfile.NamedTemporaryFile(delete=False) as temp_file:
  14.         file_path = temp_file.name
  15.    
  16.     yield file_path
  17.    
  18.     # 测试后清理
  19.     if os.path.exists(file_path):
  20.         os.remove(file_path)
  21. @pytest.fixture
  22. def sample_data():
  23.     """提供示例数据"""
  24.     return {
  25.         "name": "test",
  26.         "values": [1, 2, 3, 4, 5],
  27.         "metadata": {
  28.             "version": "1.0",
  29.             "type": "test-data"
  30.         }
  31.     }
复制代码
plugin.py 完备示例

  1. # my_python_lib/base/testbase/plugin.py
  2. from .fixture import *
  3. def pytest_configure(config):
  4.     """配置 pytest 环境"""
  5.     config.addinivalue_line(
  6.         "markers", "slow: 标记执行较慢的测试"
  7.     )
  8. def pytest_addoption(parser):
  9.     """添加命令行选项"""
  10.     parser.addoption(
  11.         "--skip-slow",
  12.         action="store_true",
  13.         default=False,
  14.         help="跳过标记为 slow 的测试"
  15.     )
  16. def pytest_collection_modifyitems(config, items):
  17.     """修改收集的测试项"""
  18.     if config.getoption("--skip-slow"):
  19.         skip_slow = pytest.mark.skip(reason="跳过慢测试 (--skip-slow 选项)")
  20.         for item in items:
  21.             if "slow" in item.keywords:
  22.                 item.add_marker(skip_slow)
复制代码
测试示例

  1. # 测试文件示例 test_utils.py
  2. import pytest
  3. import os
  4. import json
  5. def test_temp_dir_fixture(temp_dir):
  6.     """测试临时目录 fixture"""
  7.     # 在临时目录创建文件
  8.     file_path = os.path.join(temp_dir, "test.txt")
  9.     with open(file_path, "w") as f:
  10.         f.write("Hello, World!")
  11.    
  12.     # 验证文件创建成功
  13.     assert os.path.exists(file_path)
  14.     with open(file_path, "r") as f:
  15.         content = f.read()
  16.     assert content == "Hello, World!"
  17. @pytest.mark.slow
  18. def test_sample_data_manipulation(sample_data, temp_file):
  19.     """测试数据操作(标记为慢测试)"""
  20.     # 将示例数据写入临时文件
  21.     with open(temp_file, "w") as f:
  22.         json.dump(sample_data, f)
  23.    
  24.     # 读取并验证数据
  25.     with open(temp_file, "r") as f:
  26.         loaded_data = json.load(f)
  27.    
  28.     assert loaded_data == sample_data
  29.     assert loaded_data["metadata"]["version"] == "1.0"
  30.     assert sum(loaded_data["values"]) == 15
复制代码
使用方法

安装了这个 pytest 插件后,你可以在任何测试项目中直接使用这些 fixture,无需额外导入或配置:

  • 安装你的库:
    1. pip install my_python_lib
    复制代码
  • 在测试文件中直接使用 fixture:
    1. def test_file_operations(temp_dir, temp_file):
    2.     # 自动获取临时目录和临时文件
    3.     with open(temp_file, 'w') as f:
    4.         f.write('测试内容')
    5.    
    6.     assert os.path.exists(temp_file)
    复制代码
  • 使用示例数据 fixture:
    1. def test_data_processing(sample_data):
    2.     # sample_data 自动可用
    3.     assert sample_data["name"] == "test"
    4.     assert len(sample_data["values"]) == 5
    复制代码
  • 跳过慢测试:
    1. python -m pytest --skip-slow
    复制代码
  • 运行测试并检察全部可用的标志:
    1. python -m pytest --markers
    复制代码
这些 fixture 可以组合使用,也可以在本身的 conftest.py 中扩展它们,为它们提供自定义行为。
上风


  • 符合 pytest 最佳实践 - 使用官方保举的插件机制
  • 避免警告和错误 - 不再使用不保举的 pytest_plugins 方式
  • 更好的可发现性 - 自动注册 fixture,无需显式导入
  • 可配置性 - 可以添加命令行选项和配置项
  • 模块化 - 更容易维护和扩展
  • 可重用性 - 可以在多个项目中使用同一套测试工具
总结

通过将测试工具库转换为真正的 pytest 插件,我们不仅办理了特定的错误题目,还提高了代码质量和可维护性。这种方法固然前期工作量稍大,但从长远来看更加结实,尤其是当测试库必要在多个项目中使用时。

免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!更多信息从访问主页:qidao123.com:ToB企服之家,中国第一个企服评测及商务社交产业平台。
回复

使用道具 举报

0 个回复

倒序浏览

快速回复

您需要登录后才可以回帖 登录 or 立即注册

本版积分规则

我可以不吃啊

论坛元老
这个人很懒什么都没写!
快速回复 返回顶部 返回列表