【问题标题】:How to monkeypatch the environment using pytest in conftest.py?如何在 conftest.py 中使用 pytest 对环境进行猴子补丁?
【发布时间】:2017-10-13 15:31:39
【问题描述】:

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

# 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

我正在使用 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')

但是,当我在我的测试文件中 import reporter 时,我得到一个 KeyError 环境中缺少 INFLUXCLOUD_HOST。

为什么 pytest 不执行setup_env 和monkeypatch 我的环境?有办法吗?

【问题讨论】:

  • 能否请您出示您的测试文件以及您的导入报告员如何?

标签: python pytest


【解决方案1】:

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

要知道确实存在哪些测试和自动使用的夹具,pytest 需要导入测试文件和 conftest 插件。然后它扫描导入的模块,并查找fixtures & test functions & test classes & etc. 这在pytest术语中称为“集合”。

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

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

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

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

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

【讨论】:

  • 您能否提供一个示例,说明如何将客户端“打包”到夹具中?我不太明白。在模拟对os.environ[<<my_var>>] 的调用(“客户端”,如果你愿意的话)时,我仍然会收到一个键错误,因为 env 变量是在我的客户端之前加载的。
【解决方案2】:

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

(Sergey 已经在“为什么”问题上provided solid background;这试图解决“如何”。)

上下文管理器(推荐):

由于与monkeypatch 夹具不同,直接创建的实例不会自动进行undo()-ed。

# 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'

直接使用实例:

# conftest.py

from pytest import MonkeyPatch


mp = pytest.MonkeyPatch()
mp.setenv('INFLUXCLOUD_HOST', 'host')
mp.setenv('INFLUXCLOUD_USERNAME', 'username')
mp.setenv('INFLUXCLOUD_PASSWORD', 'password')

假设的文件结构供参考:

src/
    reporter.py
    __init__.py
test/
    conftest.py
    test_reporter.py

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2021-03-26
    • 2013-12-28
    • 1970-01-01
    • 2022-07-20
    • 2019-08-06
    • 2011-04-19
    • 1970-01-01
    相关资源
    最近更新 更多