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

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.LazyFunction或factory.Faker,避免硬编码 - 在
conftest.py中注册:pytest_plugins = ['pytest_factoryboy'],再用@register装饰工厂类 - 测试函数参数直接写
user(对应工厂名小写),框架自动注入一个已create()的实例;需要 build 不入库就用user__strategy=BUILD_STRATEGY - 注意 Django 场景下,
create()会真写数据库,记得用django_dbfixture 或事务回滚机制
什么时候该用 unittest.mock.Mock 而不是 MagicMock?
Mock 是最轻量的模拟对象,Mock 和 MagicMock 都支持属性访问、方法调用、返回值设定,但关键区别在于 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)更可靠










