pytest默认不加载.env文件,因未显式调用load_dotenv();最可靠方案是在conftest.py中提前加载,并注意路径与加载时机,避免变量污染或ci失效。

pytest 默认不加载 .env 文件,环境变量得手动注入或换工具处理。
为什么 pytest 运行时读不到 .env 里的变量?
因为 pytest 启动时不执行 python-dotenv 的自动加载逻辑,哪怕你项目里装了它、也放了 .env,只要没显式调用 load_dotenv(),os.getenv() 就返回 None。常见现象是测试里连数据库 URL 或密钥取不到,报 ValueError 或连接失败。
-
python-dotenv只在模块导入时自动加载一次(比如你在__init__.py里写了load_dotenv()),但pytest可能绕过该路径 - 不同运行方式行为不一致:直接
python test_x.py可能有、pytest test_x.py就没有 -
pytest的conftest.py加载时机早于你的业务模块,靠它提前load_dotenv()最可靠
在 conftest.py 中统一加载 .env 是最稳的方案
把环境变量加载逻辑塞进项目根目录或 tests 目录下的 conftest.py,确保每次 pytest 启动都执行它。注意路径要写对,否则 load_dotenv() 找不到文件。
- 在
conftest.py顶部加:from pathlib import Path from dotenv import load_dotenv <h1>假设 .env 在项目根目录(即 pytest 命令执行目录)</h1><p>env_path = Path(<strong>file</strong>).parent.parent / ".env" load_dotenv(dotenv_path=env_path)</p>
- 如果
.env在 tests 目录同级,用Path(__file__).parent / ".env" - 加
verbose=True参数可调试是否加载成功:load_dotenv(..., verbose=True),会打印“Found .env”或警告 - 避免重复加载:
load_dotenv()本身幂等,但多处调用可能干扰测试隔离性,只放在conftest.py一处
测试中需要覆盖特定环境变量时,用 pytest 的 fixture 更安全
硬编码或全局改 os.environ 容易污染其他测试。用 monkeypatch fixture 可以精准控制生命周期。
- 示例:临时覆盖数据库 URL
def test_something(monkeypatch): monkeypatch.setenv("DATABASE_URL", "sqlite:///test.db") # 此时 os.getenv("DATABASE_URL") 返回新值 assert get_db_url() == "sqlite:///test.db" -
monkeypatch在测试结束自动还原,不用手动清理 - 别用
os.environ["KEY"] = "val",这会跳过monkeypatch管理,导致变量残留 - 若需多个测试共用一组变量,定义一个自定义 fixture 并
yield,但要注意 scope(推荐scope="function")
CI/CD 或容器环境里别依赖 .env 文件
Docker、GitHub Actions 这类环境通常不挂载 .env 文件,硬依赖会导致测试在本地通过、线上失败。
- 把关键配置(如测试用 DB 地址)写进
pytest.ini或pyproject.toml,再用pytest_configurehook 注入os.environ - CI 脚本里显式
export变量,比靠load_dotenv()更可控 - 检查
os.getenv("ENVIRONMENT")是否为"test",只在非 test 环境才加载.env,避免本地开发误用生产配置
真正麻烦的不是加载不到,而是有些变量只在第一次 import 某个模块时被读取(比如 Django 的 settings.py),之后再改 os.environ 也没用——这种得在 conftest.py 里确保 load_dotenv() 发生在任何业务模块导入前。
Python免费学习笔记(深入):立即使用
在学习笔记中,你将探索 Python 的核心概念和高级技巧!











