Django中更简单的unsuspend单元测试耗时为suspend测试10倍问题
哇,这个问题确实有点反直觉——逻辑更简单的unsuspend测试反而耗时是suspend的10倍,而且你已经Mock了所有依赖,这太让人困惑了。我来帮你拆解可能的原因和验证步骤:
最可能的元凶:Mock路径错误,导致真实服务被执行
你看你的unsuspend测试里的patch路径:
@mock.patch('mc_organization.api.viewsets.organization.' 'mc_organization.services.organization.unsuspend')
拼接之后这个路径变成了mc_organization.api.viewsets.organization.mc_organization.services.organization.unsuspend,这明显不对啊!在你的视图模块mc_organization.api.viewsets.organization里,根本不存在嵌套的mc_organization子模块,所以这个Mock完全没命中目标,测试时实际调用了真实的unsuspend服务方法。
哪怕你在真实请求里看到服务调用耗时只有0.000143秒,但测试环境里真实的服务方法可能涉及数据库事务、信号触发、缓存同步这些额外操作,累加起来就导致测试耗时飙升。
对比你的suspend测试,虽然patch路径看起来类似,但可能刚好suspend的Mock路径是生效的(或者suspend的真实服务方法没有那些耗时的副作用),所以测试耗时正常。
怎么验证?
在unsuspend测试里加几行打印,看看Mock到底有没有被调用:
def test_viewset_unsuspend_action(self, get_object_mock, unsuspend_service_mock, get_serializer_mock, response_mock): # 新增验证代码 print("Is service mocked?", isinstance(unsuspend_service_mock, mock.MagicMock)) print("Call count before test:", unsuspend_service_mock.call_count) # 原测试逻辑 get_object_mock.return_value = self.organization get_serializer_mock.return_value = self.serialized_organization self.viewset.unsuspend(request=self.request, uuid=self.organization.uuid) print("Call count after test:", unsuspend_service_mock.call_count) # 原断言代码 ...
如果打印显示call_count还是0,那实锤了Mock没生效,真实方法在跑。
怎么修正?
根据你视图里调用服务的方式调整patch路径:
- 如果视图里是直接导入函数:
那patch路径应该是:from mc_organization.services.organization import unsuspend@mock.patch('mc_organization.api.viewsets.organization.unsuspend') - 如果视图里是用完整路径调用:
那patch路径应该直接指向顶层服务模块:mc_organization.services.organization.unsuspend(organization=organization)@mock.patch('mc_organization.services.organization.unsuspend')
其他可能的原因
1. 信号或post_save操作被触发
如果unsuspend服务方法里更新了数据库状态(比如把组织从暂停改为活跃),可能触发了Django的post_save或其他信号,这些信号处理函数可能有额外的耗时操作(比如更新关联数据、发送内部通知、刷新缓存)。哪怕你以为Mock了服务,但如果Mock没生效,这些真实的信号逻辑就会跑起来。
而suspend虽然逻辑更复杂,但可能对应的信号处理更少,或者你的Form Mock提前拦截了部分流程。
2. 测试调用方式的细微差异
注意到unsuspend测试调用时传了uuid参数,而suspend没传:
# unsuspend调用 self.viewset.unsuspend(request=self.request, uuid=self.organization.uuid) # suspend调用 self.viewset.suspend(request=self.request)
虽然你Mock了get_object,但DRF视图集在处理路由参数时,可能有一些底层的校验或解析逻辑没被完全Mock?不过这个可能性比较低,还是先排查Mock路径的问题更靠谱。
进一步排查工具
如果修正Mock路径后还是慢,可以用cProfile分析测试的耗时分布,精准定位哪里拖慢了速度:
import cProfile def test_viewset_unsuspend_action(self, ...): profiler = cProfile.Profile() profiler.enable() # 执行测试代码 self.viewset.unsuspend(request=self.request, uuid=self.organization.uuid) profiler.disable() profiler.print_stats(sort='cumulative')
运行测试后,你会看到每个函数的耗时占比,一眼就能找出耗时的元凶。
内容的提问来源于stack exchange,提问作者Nrzonline

