解锁 pytest_configure(config) 的神奇功能:个性化定制你的测试框架

前言

深入理解 pytest-repeat 插件的工作原理这篇文章中,我们看到pytest_repeat源码中有这样一段

scss 复制代码
def pytest_configure(config):
    config.addinivalue_line(
        'markers',
        'repeat(n): run the given test function `n` times.')
    ...

其实这里的pytest_configure也是一个钩子函数,那这个钩子函数有啥用?如何用?内部运行机制又是怎么样呢?带着这些疑问,我们一起来探索。

pytest_configure(config)是啥?

pytest_configure(config)pytest 中的一个钩子函数,它在pytest 运行前被调用一次,主要用于在测试运行之前进行配置和初始化工作。

我们看看源码中的定义:

python 复制代码
@hookspec(historic=True)
def pytest_configure(config: "Config") -> None:
    """Allow plugins and conftest files to perform initial configuration.
​
    This hook is called for every plugin and initial conftest file
    after command line options have been parsed.
​
    After that, the hook is called for other conftest files as they are
    imported.
​
    .. note::
        This hook is incompatible with ``hookwrapper=True``.
​
    :param pytest.Config config: The pytest config object.
    """

pytest 配置文件或测试文件中定义 pytest_configure 函数,pytest 会自动调用它来处理一些常规的初始化、配置和插件注册工作。在 pytest 运行过程中,pytest_configure 函数可以通过config对象来访问 pytest 的各种配置信息和操作方法。

pytest_configure(config)如何使用?

定义全局变量或常量

我们配合conftest.py一起使用。在该模块中,我们可以定义一个pytest_configure(config)` 函数,并在其中定义一些全局变量或常量。我们看示例:

arduino 复制代码
def pytest_configure(config):
    config.my_var = "hello world"

可以看到,我们定义了一个my_var变量,并赋值hello world。那在测试用例中该如何使用呢?我们继续看示例:

test_demo.py

python 复制代码
def test_my_var(pytestconfig):
    assert pytestconfig.my_var == "hello world"

可以看到,我们使用内置的 pytestconfig fixture,来访问 config 对象,这条case运行后是通过的。

注册自定义插件或hook函数

pytest_configure 函数中,你可以注册自定义的插件或 hook函数,以便在 pytest 运行过程中进行调用和处理。看案例:

arduino 复制代码
def pytest_configure(config):
    config.my_var = "hello world"
    config.pluginmanager.register(MyPlugin(), "my_plugin")

可以看到,我们使用 pytest 的插件管理器 pluginmanager 来注册一个名为MyPlugin 的插件。注册插件后,pytest 就可以根据插件的定义来进行相应的处理和调用。

test_demo.py

python 复制代码
def test_mac(pytestconfig):
    assert pytestconfig.pluginmanager.hasplugin("my_plugin")

我们这里断言是否成功注册了插件,运行通过。

除了注册插件外,还可以在 pytest_configure 函数中注册各种 pytest 的 hook 函数,以便在测试运行过程中与其它插件或 fixture 进行交互和协作。例如:

scss 复制代码
def pytest_configure(config):
    config.addinivalue_line(
    "markers", "slow: mark test as slow to run"
    )

在上述代码中,我们使用 config 对象的 addinivalue_line 方法来添加一个名为 "slow" 的标记,来标识某些测试用例需要特殊处理或忽略。

配置 pytest 各种参数和选项

在 pytest_configure 函数中,你可以配置 pytest 的各种参数和选项,以适应不同的测试需求和场景。例如:

scss 复制代码
def pytest_configure(config):
    config.addinivalue_line("markers", "unit: mark a test as a unit test")
    config.addinivalue_line("markers", "integration: mark a test as an integration test")
    config.addoption("--my-option", action="store",
        default="default-value", help="my option's help text")

在上述代码中,我们使用 config 对象的 addinivalue_line 和 addoption 方法分别添加了两个 marker 标记(用于标识测试用例)和一个自定义选项。这些配置信息可以在 pytest 运行时通过命令行参数进行覆盖或修改。

案例实现

我们现在想要实现一个需求:编写一个自定义插件,根据不同的操作系统或网络环境来选择不同的测试用例或配置参数。如何实现呢?

conftest.py

实现插件功能:

ini 复制代码
class OSPlugin:
    def pytest_collection_modifyitems(self, config, items):
        os_name = platform.system()
        if os_name == 'Windows':
            items[:] = [item for item in items if 'windows' in item.name]
        elif os_name == 'Linux':
            items[:] = [item for item in items if 'linux' in item.name]
        elif os_name == 'Darwin':
            items[:] = [item for item in items if 'mac' in item.name]
        else:
            # 对于其他操作系统,跳过所有测试用例
            items.clear()
​
    def pytest_configure(self, config):
        print(f"Running tests on {platform.system()}")

这段代码,我们创建了一个名为 OSPlugin 的自定义插件类。它实现了 pytest_collection_modifyitems 方法,在收集测试用例之后修改测试用例项。根据当前操作系统的不同,我们可以选择性地保留或跳过特定的测试用例。

我们还实现了 pytest_configure 方法,用于打印正在运行测试的操作系统信息。

注册自定义插件:

scss 复制代码
def pytest_configure(config):
    config.pluginmanager.register(OSPlugin(), "os_plugin")

测试case:

test_demo.py

python 复制代码
def test_windows_only():
    assert True
​
def test_linux_only():
    assert True
​
def test_mac_case():
    assert True

笔者是mac系统,执行结果如下:

ini 复制代码
Running tests on Darwin
============================= test session starts ==============================
collecting ... Darwin
collected 3 items
​
test_demo.py::test_mac_case 
​
============================== 1 passed in 0.13s ===============================

可以看到,当你运行这些测试用例时,根据不同的操作系统,将会选择性地执行特定测试用例或跳过所有测试用例。

运行机制

到这里应该能够正确使用该函数了,我们再看一下它的运行机制是怎样的呢?

这个插件钩子函数的主要作用是对 config 对象进行配置,其中 config 是一个 pytest 的配置对象,提供了对 pytest 运行过程中的配置项进行读取和修改的接口。

  1. pytest 启动时,会自动加载所有已注册的插件。
  2. pytest 开始配置过程,遍历已加载的插件,依次调用每个插件的 pytest_configure 方法。
  3. 通过调用 pytest_configure(config) 方法,插件可以访问和修改 config 对象来进行配置。
  4. 插件可以使用 config. 前缀来访问或修改 config 对象的属性和方法。

最后

pytest_configure(config) 钩子函数是一个非常灵活和强大的 pytest 扩展机制,可以用来进行各种初始化、配置、插件注册等工作。可以使用 pytest_configure 函数来处理各种复杂的测试需求和场景,从而提高测试代码的可读性和可维护性。很大三方插件都使用了该钩子函数,比如开头提到的pytest_repeat插件。另外,我们在案例实现中使用了pytest_collection_modifyitems()钩子函数,我们之后会写一篇文章来讲解该钩子函数的使用原理。

相关推荐
denghai邓海20 分钟前
红黑树删除之向上调整
python·b+树
封步宇AIGC1 小时前
量化交易系统开发-实时行情自动化交易-3.4.1.2.A股交易数据
人工智能·python·机器学习·数据挖掘
何曾参静谧1 小时前
「Py」Python基础篇 之 Python都可以做哪些自动化?
开发语言·python·自动化
Prejudices1 小时前
C++如何调用Python脚本
开发语言·c++·python
我狠狠地刷刷刷刷刷1 小时前
中文分词模拟器
开发语言·python·算法
Jam-Young1 小时前
Python的装饰器
开发语言·python
man20171 小时前
【2024最新】基于springboot+vue的闲一品交易平台lw+ppt
vue.js·spring boot·后端
Mr.咕咕1 小时前
Django 搭建数据管理web——商品管理
前端·python·django
hlsd#2 小时前
关于 SpringBoot 时间处理的总结
java·spring boot·后端
路在脚下@2 小时前
Spring Boot 的核心原理和工作机制
java·spring boot·后端