提问人:Amin Ba 提问时间:3/10/2022 最后编辑:Amin Ba 更新时间:11/13/2023 访问量:1311
如何在导入所有内容之前运行 pytest conftest
how to run pytest conftest before everything is imported
问:
这是我问题的简化版本。
我有一个 python 应用程序,其结构如下:
my_project
my_app
__init__.py
settings.py
tests
__init__.py
conftest.py
my_test.py
venv
# settings.py
from dotenv import load_dotenv
load_dotenv()
MY_VALUE = os.environ["MY_KEY"]
出于某些原因,我不想在这里添加 .env
文件。我也不想在os.environ
上使用get方法
我想运行此测试
# my_test.py
from my_app import settings # I need this for some reasons
def test_something():
assert True
def test_env():
assert os.environ["MY_KEY"] == 'MY_VALUE'
但是,我得到一个,因为一旦我导入,该行就会运行,并且因为不在环境中,我得到.KeyError
settings.py
MY_VALUE = os.environ["MY_KEY"]
MY_KEY
KeyError
我的想法是,我可以像下面这样解释
import os
from unittest import mock
import pytest
@pytest.fixture(autouse=True)
def set_env(config):
with mock.patch.dict(os.environ, {'MY_KEY': 'MY_VALUE'}):
yield
但这仍然是同样的问题。
我怎样才能先执行这个 conftest。
还要注意,如果我注释掉,我的两个测试都会通过from my_app import settings
注意:我不想使用的原因是我想在应用程序或测试初始化之前强制 env 变量存在。这样做的原因是,实际上,当我的应用程序在 docker 容器中运行时,env 变量先于其他所有变量存在,我想增加 dev-prod 奇偶校验。https://12factor.net/dev-prod-parityos.environ.get("MY_KEY")
答:
发生这种情况的原因是 pytest 首先收集所有测试,然后运行它们。
在收集阶段,pytest 导入 和 .conftest.py
my_test.py
在测试运行阶段,pytest 会应用夹具并自行运行测试函数。
在您的情况下,在收集阶段,imports ,导致读取环境变量的副作用。由于固定装置尚未应用,因此提出了 a。my_test.py
my_app/settings.py
os.environ["MY_KEY"]
KeyError
有两种方法可以解决此问题:
1. 构建没有副作用的代码。
无需初始化模块内部的设置,而是可以将初始化移动到函数中。此外,还可用于确保仅加载一次配置。get_settings
functools.cache
# my_app/settings.py
import functools
from dotenv import load_dotenv
@functools.cache
def get_settings():
load_dotenv()
MY_VALUE = os.environ["MY_KEY"]
return {"MY_KEY": MY_VALUE}
# tests/conftest.py
import os
from unittest import mock
import pytest
@pytest.fixture(autouse=True)
def set_env():
with mock.patch.dict(os.environ, {'MY_KEY': 'MY_VALUE'}):
yield
# tests/my_test.py
from my_app import get_setting
def test_env():
settings = get_setting()
assert settings["MY_KEY"] == 'MY_VALUE'
2. 延迟导入settings.py
如果第一点不适用,可以在测试中导入。这样,它将在应用夹具后的试运行阶段导入。settings.py
# my_app/settings.py
from dotenv import load_dotenv
load_dotenv()
MY_VALUE = os.environ["MY_KEY"]
# tests/conftest.py
import os
from unittest import mock
import pytest
@pytest.fixture(autouse=True)
def set_env():
with mock.patch.dict(os.environ, {'MY_KEY': 'MY_VALUE'}):
yield
# tests/my_test.py
def test_env():
from my_app import settings
assert settings.MY_KEY == 'MY_VALUE'
请注意,Python 将在首次导入后缓存设置。如果要使用不同的设置运行多个测试,可以:
# tests/conftest.py
import os
import importlib
from unittest import mock
import pytest
@pytest.fixture
def set_env_1():
with mock.patch.dict(os.environ, {'MY_KEY': 'MY_VALUE_1'}):
yield
@pytest.fixture
def set_env_2():
with mock.patch.dict(os.environ, {'MY_KEY': 'MY_VALUE_2'}):
yield
@pytest.fixture
def settings():
from py_helpers import settings
return importlib.reload(settings)
# tests/my_test.py
def test_env_my_key_1(set_env_1, settings):
assert settings.MY_KEY == 'MY_VALUE_1'
def test_env_my_key_2(set_env_2, settings):
assert settings.MY_KEY == 'MY_VALUE_2'
评论