如何在pytest中断言含mock属性的两个Pydantic模型相等?
优雅处理Pydantic模型中忽略特定字段的断言测试
我明白你遇到的问题——当测试Pydantic模型列表时,总有一些动态计算的字段(比如你的est_time)不在测试范围内,直接用mock.ANY替代会因为类型/引用不匹配导致断言失败,而现在循环对比字典的写法又有点啰嗦。下面给你几个更简洁、符合Python风格的解决方案:
方案1:用Pydantic自带的exclude参数跳过字段
这应该是最直接的方案,利用Pydantic模型转字典时的exclude参数,直接忽略不需要校验的est_time字段:
expected_list = [ Class.construct( student_id=student1.id, property=Property.A, ), Class.construct( student_id=student1.id, property=Property.B, ) ] for result, expected in zip(my_schedules, expected_list): # 排除est_time后再对比字典 assert result.dict(exclude={"est_time"}) == expected.dict(exclude={"est_time"})
如果后续需要忽略多个字段,只需要在exclude的字典里添加对应的键即可,逻辑清晰且无需额外依赖。
方案2:自定义模型比较方法(适合频繁复用场景)
如果你的测试中经常需要忽略est_time做断言,可以给Class模型添加一个自定义的比较方法,把固定要校验的字段逻辑封装起来:
from pydantic import BaseModel from datetime import datetime from enum import Enum class Property(Enum): A = "A" B = "B" class Class(BaseModel): student_id: str property: Property est_time: datetime def matches_without_est_time(self, other: "Class") -> bool: """忽略est_time字段,对比其他核心属性""" return ( self.student_id == other.student_id and self.property == other.property )
之后断言的时候就可以直接调用这个方法,代码更易读:
expected_list = [ Class.construct(student_id=student1.id, property=Property.A), Class.construct(student_id=student1.id, property=Property.B), ] for result, expected in zip(my_schedules, expected_list): assert result.matches_without_est_time(expected)
方案3:手动清理字典字段(轻量灵活)
如果不想修改模型也不想用Pydantic的参数,也可以直接在对比前删除est_time字段,写法同样简洁:
expected_list = [ {"student_id": student1.id, "property": Property.A.value}, {"student_id": student1.id, "property": Property.B.value}, ] for result, expected in zip(my_schedules, expected_list): result_dict = result.dict() del result_dict["est_time"] assert result_dict == expected
这里注意把枚举类型转成对应的值(比如Property.A.value),避免类型不匹配。
方案4:用pytest的approx处理动态时间(适合有微小误差的场景)
如果你的est_time是基于当前时间生成的,可能存在毫秒级的误差,这时候可以用pytest的approx来做近似对比:
from pytest import approx expected_list = [ Class.construct( student_id=student1.id, property=Property.A, est_time=datetime(2020,1,3), # 预期的基准时间 ), Class.construct( student_id=student1.id, property=Property.B, est_time=datetime(2020,1,4), ) ] for result, expected in zip(my_schedules, expected_list): # 转成时间戳后允许微小误差 assert result.est_time.timestamp() == approx(expected.est_time.timestamp()) # 其他字段直接对比 assert result.student_id == expected.student_id assert result.property == expected.property
这个方案适合需要校验est_time大致范围,而不是完全忽略的场景。
总结
如果只是单纯忽略est_time字段,方案1是最简洁且符合Pydantic设计的选择;如果需要频繁做这类断言,方案2的复用性更好;方案3则是最轻量的临时写法。
内容的提问来源于stack exchange,提问作者zcahfg2
相关产品推荐
相关产品推荐

