You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何在Pytest中Patch Pydantic BaseSettings的Annotated类型验证器?

关于Pydantic BaseSettings中Annotated类型BeforeValidator的Patch问题

能不能Patch原代码中的BeforeValidator方法?

不行。因为Pydantic在解析Annotated类型时,会立即捕获BeforeValidator传入的函数引用并保存,后续patch模块级别的asd_func只会替换模块中的函数变量,无法改变Pydantic已经持有的原始函数引用,验证逻辑不会被替换。

为什么当前代码无法运行?

没错,核心原因就是你猜测的那样:当AsdType = Annotated[str, BeforeValidator(asd_func)]执行时,BeforeValidator已经把当时的asd_func引用存储到了类型定义中。后续的patch操作只是修改了模块里的asd_func变量指向,但Pydantic处理Config类字段验证时,调用的还是之前保存的原始函数,所以config.ASD仍然是原函数返回的"asd",断言失败。

如何重构让代码正常工作?

根据你跳过磁盘路径检查的测试需求,提供三种实用方案:

方案1:使用字符串引用函数(推荐)

Pydantic支持用字符串形式指定验证函数,它会在运行时动态从模块中查找函数,此时patch就能生效:

from typing import Annotated
from unittest.mock import patch

from pydantic import BeforeValidator
from pydantic_settings import BaseSettings


def asd_func(value):
    return value


# 用字符串引用函数,而非直接传入函数对象
AsdType = Annotated[str, BeforeValidator("asd_func")]


class Config(BaseSettings):
    ASD: AsdType = "asd"


@patch(__name__ + ".asd_func", return_value="zzzzzz")
def test_config_load(_):
    config = Config()
    assert config.ASD == "zzzzzz"  # 断言成功


test_config_load()

方案2:测试时直接修改模型字段的验证器

如果不想修改原类型定义,可以在测试中临时替换Config字段的验证器列表:

from typing import Annotated
from unittest.mock import patch

from pydantic import BeforeValidator
from pydantic_settings import BaseSettings


def asd_func(value):
    return value


AsdType = Annotated[str, BeforeValidator(asd_func)]


class Config(BaseSettings):
    ASD: AsdType = "asd"


def test_config_load():
    # 保存原有验证器,测试后恢复
    original_validators = Config.model_fields["ASD"].validators
    try:
        # 清空验证器,跳过检查
        Config.model_fields["ASD"].validators = []
        config = Config()
        assert config.ASD == "asd"

        # 或者替换为mock验证逻辑
        def mock_func(v):
            return "zzzzzz"
        Config.model_fields["ASD"].validators = [BeforeValidator(mock_func)]
        config = Config()
        assert config.ASD == "zzzzzz"
    finally:
        Config.model_fields["ASD"].validators = original_validators


test_config_load()

方案3:将验证函数作为类依赖注入

把验证函数绑定到Config类的属性上,方便直接patch类属性:

from typing import Annotated
from unittest.mock import patch

from pydantic import field_validator
from pydantic_settings import BaseSettings


def asd_func(value):
    return value


class Config(BaseSettings):
    ASD: str = "asd"

    @field_validator("ASD")
    def validate_asd(cls, value):
        return cls._asd_validator(value)

    # 定义类属性存储验证函数,便于patch
    _asd_validator = staticmethod(asd_func)


@patch.object(Config, "_asd_validator", return_value="zzzzzz")
def test_config_load(_):
    config = Config()
    assert config.ASD == "zzzzzz"  # 断言成功


test_config_load()

内容的提问来源于stack exchange,提问作者mrc

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.12 05:40:15