我的主文件中有一个全局对象
# 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)
这里的问题在于误解了会话范围的装置是什么。
要知道哪些测试和自动使用的装置确实存在,pytest 需要导入测试文件和 conftest 插件。然后它扫描导入的模块,并查找夹具、测试函数和测试类等。这在 pytest 术语中称为“集合”。
只有在收集了所有测试之后,pytest 才决定执行它们,并安排执行计划,特别是在准备好夹具的时候。会话范围的装置首先准备,最后拆除——在任何测试开始之前,以及在所有测试完成之后。
但是,测试文件和 conftest 的导入假定这些模块的执行——作为任何其他 Python 模块的导入,与 pytest 无关。
所以,当你做 import reporter从测试文件执行此操作时,或者即使将该全局变量直接放入测试文件,也会执行此模块,并尝试使用环境变量。但是夹具还没有执行(而且 pytest 还不知道它们的存在)。因此,它失败了。
即使您import reporter从测试函数内部,这也无济于事,因为 pytest 可能会reporter.py在收集阶段之前尝试导入该模块。由于缺少测试函数/类,Pytest 会过滤掉它,但导入尝试将完成并且将失败。
这里最好的解决方案是将客户端“打包”到一个夹具中并使用该夹具而不是全局变量。