应使用 unittest.mock.patch 替换异步函数调用点(被导入位置而非定义位置),配合 asyncmock 拦截 await 调用,patch asyncio.sleep 实现立即返回,并通过 assert_called_once_with 等校验调用细节。

用 unittest.mock.patch 拦截真实异步调用
直接运行耗时的后台任务会让测试变慢、不稳定,还可能触发外部依赖(比如发邮件、调用API)。核心思路不是等它执行完,而是让它「不真跑」。最常用也最稳妥的方式是用 patch 替换掉实际的异步函数或任务调度入口。
常见错误是只 patch 函数定义处(比如 @patch('myapp.tasks.send_notification')),但实际调用发生在 myapp.views 里——这时必须 patch myapp.views.send_notification,即「被导入的位置」,不是「被定义的位置」。
实操建议:
- 优先 patch 任务的「触发点」,比如
celery_task.delay()或asyncio.create_task()的调用位置 - 若用 Celery,可设
task_always_eager=True配置让任务同步执行,但注意这仍会真正执行函数体,只是不走 broker;想跳过执行,还得配合patch - 对
async def函数,用AsyncMock(Python 3.8+)替代普通Mock,否则 await 会报TypeError: object Mock can't be used in 'await' expression
验证异步任务是否被正确调度
光拦截不够,得确认任务确实按预期被调用了:参数对不对、调用次数对不对、有没有在特定条件下跳过。这时候不能只看返回值,得检查 mock 对象的状态。
典型场景包括:用户注册后触发发送欢迎邮件,但仅当邮箱域名不是 @test.com 时才发。
实操建议:
- 用
mocked_task.delay.assert_called_once_with(user_id=123)断言调用细节 - 若任务被多次调度(比如重试逻辑),用
assert_called_with()配合call_args_list检查每次参数 - 避免写
assert mocked_task.delay.called—— 这种布尔判断丢失上下文,出错时看不出哪里没调;改用assert_not_called()或带参数的断言
处理 asyncio.sleep() 或重试等待逻辑
很多后台任务内部有 await asyncio.sleep(5) 或基于 asyncio.wait_for() 的超时控制。这些会让测试卡住或超时失败。
关键不是「加速 sleep」,而是「让 sleep 立刻完成」——通过 patch asyncio.sleep 并让它协程立刻返回。
实操建议:
- 用
@patch('asyncio.sleep', new_callable=AsyncMock),再在测试中设mock_sleep.return_value = None - 若任务里写了
await asyncio.sleep(300)却没被 patch,测试会真的等 5 分钟;CI 环境通常设了全局 timeout,直接导致测试被杀 - 注意:不要 patch
time.sleep,它和asyncio.sleep完全无关;混用会导致 patch 失效且无报错
测试异常路径:任务失败、重试、降级逻辑
后台任务常带重试机制(如 Celery 的 autoretry_for,或手写的 while attempt 循环),或者失败后触发告警/降级处理。这类逻辑很难靠真实失败来测,因为网络抖动不可控、异常难复现。
实操建议:
- 让被 patch 的任务函数首次抛异常,第二次返回成功,模拟「重试一次后成功」:用
side_effect=[Exception('conn err'), {'status': 'ok'}] - 若任务里调用了
logger.error或发 Slack 告警,同样 patch 它们并断言是否被调用,验证降级路径是否走通 - 避免在测试里用
try/except包裹 await 调用再手动 assert 异常——这绕过了框架自身的异常处理逻辑,测的不是真实行为
最难的不是写 mock,而是搞清「谁在什么时候调用了什么」。一个后台任务从 HTTP 请求触发,到进队列、worker 拉取、执行、重试、记录日志,中间至少跨 3–4 层模块。漏 patch 任意一层,测试就可能跑飞或误判。建议先加一句 print 或断点,跑通一次真实流程,再逐层替换成 mock。
Python免费学习笔记(深入):立即使用
在学习笔记中,你将探索 Python 的核心概念和高级技巧!











