如何在conftest.py中使用pytest修补环境?

Mil*_*mak 6 python pytest

我的主文件中有一个全局对象

# reporter.py

from os import environ
from influxdb import InfluxDBClient

influxdb_client = InfluxDBClient(host=environ['INFLUXCLOUD_HOST'],
                                 username=environ['INFLUXCLOUD_USERNAME'],
                                 password=environ['INFLUXCLOUD_PASSWORD'],
                                 ssl=True,
                                 timeout=4*60)

def foo():
    pass
Run Code Online (Sandbox Code Playgroud)

我正在使用pytest,并且想为这些环境变量设置虚假值。我的conftest.py中包含以下内容:

# conftest.py

import pytest

@pytest.fixture(scope='session', autouse=True)
def setup_env(monkeypatch):
    monkeypatch.setenv('INFLUXCLOUD_HOST', 'host')
    monkeypatch.setenv('INFLUXCLOUD_USERNAME', 'username')
    monkeypatch.setenv('INFLUXCLOUD_PASSWORD', 'password')
Run Code Online (Sandbox Code Playgroud)

但是,当我import reporter在测试文件中时,KeyError发现环境中缺少INFLUXCLOUD_HOST。

为何pytest不执行setup_env我的环境并进行monkeypatch?有办法吗?

Nat*_*nes 11

pytest 6.2开始,您可以直接使用MonkeyPatch对象而不是monkeypatch固定装置,作为实例或上下文管理器。

(谢尔盖已经提供了关于“为什么”问题的扎实背景;这试图解决“如何”。)

上下文管理器(推荐):

因为与monkeypatch固定装置不同,直接创建的实例不会undo()自动进行编辑。

# test_reporter.py

from pytest import MonkeyPatch


def test_get_client_username():
    with MonkeyPatch.context() as mp:
        mp.setenv('INFLUXCLOUD_HOST', 'host')
        mp.setenv('INFLUXCLOUD_USERNAME', 'username')
        mp.setenv('INFLUXCLOUD_PASSWORD', 'password')

        from src.reporter import influxdb_client

        assert influxdb_client._username == 'username'
Run Code Online (Sandbox Code Playgroud)

直接使用实例:

# conftest.py

from pytest import MonkeyPatch


mp = pytest.MonkeyPatch()
mp.setenv('INFLUXCLOUD_HOST', 'host')
mp.setenv('INFLUXCLOUD_USERNAME', 'username')
mp.setenv('INFLUXCLOUD_PASSWORD', 'password')
Run Code Online (Sandbox Code Playgroud)

假设的文件结构供参考:

src/
    reporter.py
    __init__.py
test/
    conftest.py
    test_reporter.py
Run Code Online (Sandbox Code Playgroud)


Ser*_*yev 7

这里的问题在于误解了会话范围的装置是什么。

要知道哪些测试和自动使用的装置确实存在,pytest 需要导入测试文件和 conftest 插件。然后它扫描导入的模块,并查找夹具、测试函数和测试类等。这在 pytest 术语中称为“集合”。

只有在收集了所有测试之后,pytest 才决定执行它们,并安排执行计划,特别是在准备好夹具的时候。会话范围的装置首先准备,最后拆除——在任何测试开始之前,以及在所有测试完成之后。

但是,测试文件和 conftest 的导入假定这些模块的执行——作为任何其他 Python 模块的导入,与 pytest 无关。

所以,当你做 import reporter从测试文件执行此操作时,或者即使将该全局变量直接放入测试文件,也会执行此模块,并尝试使用环境变量。但是夹具还没有执行(而且 pytest 还不知道它们的存在)。因此,它失败了。

即使您import reporter从测试函数内部,这也无济于事,因为 pytest 可能会reporter.py在收集阶段之前尝试导入该模块。由于缺少测试函数/类,Pytest 会过滤掉它,但导入尝试将完成并且将失败。

这里最好的解决方案是将客户端“打包”到一个夹具中并使用该夹具而不是全局变量。

  • 您能否提供一个示例,说明如何将客户端“打包”到夹具中?我不太明白。在模拟对`os.environ[<<my_var>>]` 的调用(“客户端”,如果你愿意的话)时,我仍然收到一个关键错误,因为在我的客户端之前加载了环境变量。 (6认同)