Python 单元测试中的假数据构造技巧

1次阅读

mock.patch没生效的根本原因是patch位置错误,必须patch被导入的地方而非定义的地方,例如在service.py中导入了send_email,则应patch’service.send_email’而非’utils.send_email’。

Python 单元测试中的假数据构造技巧

mock.patch 为什么没生效?

常见现象是打了 patch,但被测函数里调用的还是真实对象。根本原因通常是 patch 的位置不对——必须 patch「被导入的地方」,而不是定义的地方。

  • 比如模块 utils.py 定义了 send_email(),而你在 service.py 中写了 from utils import send_email,那么测试时得 @mock.patch('service.send_email'),不是 @mock.patch('utils.send_email')
  • 如果用的是 import utils,那就要 patch 'service.utils',再通过 utils.send_email 访问
  • 类方法 patch 要注意:patch 实例方法时,目标是类所在模块中的引用路径,且 mock 对象会自动传入 self

factory_boy 和 pytest-factoryboy 怎么配合用?

直接写 Faker().name() 看似快,但数据不可控、难复现;手写 dict 又容易散落在各处。用 factory_boy 是为了统一构造逻辑,而 pytest-factoryboy 把它变成 fixture,省去每次 build()create() 的重复代码。

  • 定义 UserFactory 时,字段尽量用 factory.LazyFunctionfactory.Faker,避免硬编码
  • conftest.py 中注册:pytest_plugins = ['pytest_factoryboy'],再用 @register 装饰工厂类
  • 测试函数参数直接写 user(对应工厂名小写),框架自动注入一个已 create() 的实例;需要 build 不入库就用 user__strategy=BUILD_STRATEGY
  • 注意 django 场景下,create() 会真写数据库,记得用 django_db fixture 或事务回滚机制

什么时候该用 unittest.mock.Mock 而不是 MagicMock?

Mock 是最轻量的模拟对象,MockMagicMock 都支持属性访问、方法调用、返回值设定,但关键区别在于 magic method(如 __len____iter__)是否默认可调用。

  • 如果你只是模拟一个普通函数或简单对象,用 Mock 更安全:它不会意外响应 len(obj) 这类操作,报 TypeError 反而帮你早点发现问题
  • 要 mock 一个列表行为(比如让 mock 支持 for x in obj:),必须用 MagicMock,或者手动给 Mock 设置 __iter__ 返回迭代器
  • Django QuerySet 常见坑:直接 Mock(return_value=[...]) 不行,因为 QuerySet 被当成布尔值(if qs:)时会触发 __bool__,这时得用 MagicMock 或显式设置 __bool__ = Mock(return_value=True)

fixture 太重,想局部构造假数据怎么办?

不是所有测试都需要完整模型实例。过度依赖 factory 或 DB fixture 会让单测变慢、失败时定位困难。局部构造的关键是「最小够用」+「类型对齐」。

立即学习Python免费学习笔记(深入)”;

  • 字典代替模型:只要函数只读字段,传 {'id': 1, 'name': 'test'} 就够,别硬造 model 实例
  • namedtuple 比 dict 更稳妥:能防字段拼写错误,又比 dataclass 轻,比如 User = namedtuple('User', ['id', 'email'])
  • types.SimpleNamespace 快速建带属性的对象:SimpleNamespace(id=1, is_active=True),支持点号访问,无额外行为干扰
  • 警惕「假数据太真」:比如给 datetime 字段塞 timezone.now(),会导致断言时间漂移;固定用 datetime(2023, 1, 1, tzinfo=UTC) 更可靠

复杂点往往藏在 patch 路径和对象生命周期里——mock 对象一旦被赋值给模块级变量,后续测试可能复用旧实例;factory 构造的数据若带外键或信号,不清理就容易污染下一个测试。这些细节不报错,但会让测试偶尔失败。

text=ZqhQzanResources