You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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插件的mocker fixture,两者属于两套独立的Mock方案,混用会导致参数接收、Mock对象赋值逻辑混乱。
解决方案

方案1:使用pytest-mock(推荐,符合pytest风格)

  1. 先确认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
  2. 去掉@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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.10.02 20:57:02