如何在pytest中正确模拟环境变量以解决Pydantic验证错误?
正确模拟Pydantic环境变量的解决方案
问题根源
你的Config类中,auth: ClassVar[ServiceConfig] = ServiceConfig()是模块加载阶段就执行的代码——这意味着在你的测试补丁生效前,ServiceConfig已经读取了当时的环境变量(此时API_AUDIENCE未设置,触发ValidationError)。之前的两种补丁方式都太晚,无法覆盖模块初始化时的环境变量读取操作。
三种可行解决方案
1. 在模块加载前提前设置环境变量(unittest场景)
通过在测试类的setUpClass中先补丁环境变量,再导入Config类,确保ServiceConfig初始化时能读取到模拟值:
import unittest from unittest import mock import os # 不要提前导入Config类 class TestConfig(unittest.TestCase): @classmethod def setUpClass(cls): # 先补丁环境变量 cls.patcher = mock.patch.dict(os.environ, {"API_AUDIENCE": "https://mock.com"}) cls.patcher.start() # 此时再导入Config,ServiceConfig会读取已设置的环境变量 global Config from config import Config @classmethod def tearDownClass(cls): cls.patcher.stop() def test_api_audience(self): self.assertEqual(Config.auth.API_AUDIENCE, "https://mock.com")
2. 延迟初始化ServiceConfig(修改源码适配测试)
修改Config类,避免模块级别的提前实例化,改为延迟加载:
# config.py from pydantic import BaseSettings, ClassVar import os class ServiceConfig(BaseSettings): API_AUDIENCE: str = os.environ.get("API_AUDIENCE") class Config(BaseSettings): _auth: ClassVar[ServiceConfig] = None @classmethod @property def auth(cls): if cls._auth is None: cls._auth = ServiceConfig() return cls._auth
测试时先补丁环境变量,再访问Config.auth:
from unittest import mock from config import Config def test_mocked_audience(): with mock.patch.dict(os.environ, {"API_AUDIENCE": "https://mock.com"}): # 此时才会初始化ServiceConfig assert Config.auth.API_AUDIENCE == "https://mock.com"
3. 直接替换ServiceConfig实例(无需环境变量补丁)
跳过环境变量模拟,直接创建ServiceConfig的测试实例并替换Config.auth:
from unittest import mock from config import Config, ServiceConfig def test_direct_instance(): mock_auth = ServiceConfig(API_AUDIENCE="https://mock.com") with mock.patch.object(Config, 'auth', mock_auth): assert Config.auth.API_AUDIENCE == "https://mock.com"
为什么之前的方法失败
- 第一种
@mock.patch.dict装饰测试类:补丁在测试类加载后才生效,但Config早已在模块导入时完成了ServiceConfig的初始化,读取的是未补丁的环境变量。 - 第二种
@patch('config.Config.API_AUDIENCE'):Config本身没有API_AUDIENCE属性,该属性属于Config.auth(即ServiceConfig实例),且Pydantic字段默认不可直接赋值修改,因此补丁无效。
内容的提问来源于stack exchange,提问作者GustaMan9000
相关产品推荐
相关产品推荐

