Django项目使用pytest单元测试mock时出现ModuleNotFoundError如何解决
报错原因
- 路径结构不匹配:错误提示明确说明
smspanel.services是单个.py文件(模块),不是文件夹形式的包,你写的patch路径smspanel.services.sms_panel_info_service.get_buinessman_sms_panel将services识别为包,尝试从该包下加载sms_panel_info_service子模块,和实际项目结构不符。 - Mock路径规则错误:单元测试Mock的核心规则是 Mock被测代码实际导入使用的位置,而非方法定义的原始位置,如果
send_plain_sms方法是在smspanel/services.py中定义,并且在该文件内导入了get_buinessman_sms_panel调用,那你需要Mock的是services.py中实际使用的该方法的导入路径,而非原始定义路径。 - Mock语法混用:你同时使用了unittest自带的
@patch装饰器,和pytest-mock插件的mockerfixture,两者属于两套独立的Mock方案,混用会导致参数接收、Mock对象赋值逻辑混乱。
解决方案
方案1:使用pytest-mock(推荐,符合pytest风格)
- 先确认
send_plain_sms所在的services.py中get_buinessman_sms_panel的导入名称:- 如果
services.py中是from xxx import get_buinessman_sms_panel的导入形式,Mock路径写smspanel.services.get_buinessman_sms_panel - 如果
services.py中是import sms_panel_info_service后通过sms_panel_info_service.get_buinessman_sms_panel()调用的形式,Mock路径写smspanel.services.sms_panel_info_service.get_buinessman_sms_panel
- 如果
- 去掉
@patch装饰器,改用mocker的patch方法,修改后的测试代码示例:
def test_send_plain_sms_success(mocker, businessman_with_customer_tuple): # 替换为上一步确认的正确Mock路径 mock_get_panel = mocker.patch('smspanel.services.get_buinessman_sms_panel') mock_get_panel.return_value = SMSPanelInfo() # 其余测试逻辑保持不变 customer_ids = list(map(lambda e: e.id, businessman_with_customer_tuple[1])) businessman = businessman_with_customer_tuple[0] message = 'message' result = services.send_plain_sms(user=businessman, customer_ids=customer_ids, message=message) smsq = SMSMessage.objects.filter( businessman=businessman, message_type=SMSMessage.TYPE_PLAIN, status=SMSMessage.STATUS_PENDING, message=message) assert smsq.exists() sms = smsq.first() receivers_count = SMSMessageReceivers.objects.filter(sms_message=sms, customer__id__in=customer_ids).count() assert receivers_count == len(customer_ids)
方案2:使用unittest的@patch装饰器
如果要保留@patch的写法,去掉参数里的mocker,修改路径即可,示例:
@patch('smspanel.services.get_buinessman_sms_panel') # 替换为正确路径 def test_send_plain_sms_success(mock_get_panel, businessman_with_customer_tuple): mock_get_panel.return_value = SMSPanelInfo() # 其余测试逻辑保持不变
内容的提问来源于stack exchange,提问作者Pouya Kermanshahi
相关产品推荐
相关产品推荐

