是,pytest-env 能注入环境变量,它在 pytest_configure 钩子中将配置值写入 os.environ,对测试函数内 os.getenv() 有效,但对模块导入时读取环境变量的代码无效。

pytest-env 插件是否真能注入环境变量?
不能直接注入,它只读取配置并设置 os.environ —— 但这个行为发生在 pytest 启动早期,**对测试函数内调用的 os.getenv() 有效,对模块级导入时读取环境变量的代码无效**。比如你在 settings.py 里写了 DEBUG = os.getenv("DEBUG") == "1",而该文件在 conftest.py 加载前就被 import 了,那 pytest-env 设置的值就赶不上。
如何正确配置 pytest-env 的 .env 文件和 pytest.ini
插件依赖两个地方协同生效:一个是项目根目录下的 .env 文件(不是 shell 的那种),另一个是 pytest.ini 中的 [tool:pytest] 配置段。
-
.env文件内容必须是纯键值对,不支持注释、引号或空格环绕:API_URL=https://test.example.com LOG_LEVEL=DEBUG FEATURE_FLAG=true
-
pytest.ini必须显式启用插件并指定路径:[tool:pytest] env_files = .env env_ignore_local = false
- 如果
.env不在项目根目录,需写相对路径,如tests/.env;多个文件用逗号分隔:env_files = .env,tests/env.test
为什么测试里读不到刚设的环境变量?
常见原因不是插件没起作用,而是你读取时机太早。pytest-env 在 pytest_configure 钩子中加载 .env,此时测试函数还没执行,但很多框架(Django、FastAPI、SQLAlchemy)会在模块导入阶段就解析环境变量。
- 验证是否生效:在测试函数开头加
print(os.environ.get("API_URL")),能看到值说明插件工作正常 - 模块级变量失效的典型场景:
config.py中DB_URL = os.getenv("DB_URL")—— 这个值在测试 importconfig时就固定了,后续改os.environ也无效 - 解决办法只有两个:
reload(config)(不推荐,副作用大),或把环境变量读取逻辑推迟到函数/fixture 内部
替代方案:用 fixture 手动控制环境变量更可靠
当需要精确控制某几个测试的环境变量,或要避免全局污染时,monkeypatch fixture 比 pytest-env 更可控、更易调试。
立即学习“Python免费学习笔记(深入)”;
def test_with_custom_api_url(monkeypatch):
monkeypatch.setenv("API_URL", "https://mock.example.com")
monkeypatch.setenv("DEBUG", "1")
assert get_client().base_url == "https://mock.example.com"
-
monkeypatch在测试结束后自动还原,不会影响其他测试 - 它修改的是当前进程的
os.environ,对任何后续调用都立即生效 - 适合做差异化测试,比如一个测试跑 prod 环境 URL,另一个跑 staging
真正麻烦的从来不是“怎么设”,而是“什么时候设才来得及”。模块导入顺序、配置加载时机、pytest 插件生命周期——这些才是让环境变量在测试里“消失”的真实原因。


















