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

Django 4.2.6单元测试中如何Mock信号函数do_stuff?

Django信号处理函数Mock失效的解决方法

问题原因

你遇到的Mock失效,核心原因是Django信号系统保存的是函数的直接引用:
当执行pre_save.connect(do_stuff, sender=MyEntity)时,Django会把do_stuff函数的实际引用存入信号接收器列表。后续你通过@mock.patch("application.package.signals.do_stuff")替换的只是模块中do_stuff变量的指向,已经绑定到信号里的原函数引用并不会被替换,所以触发信号时依然会执行原逻辑。

可行解决方案

方案一:断开原信号连接,替换为Mock函数

在测试流程中先断开原有信号绑定,再将Mock函数连接到信号上,测试结束后恢复原连接:

from django.test import TestCase
from unittest.mock import Mock
from django.db.models.signals import pre_save
from application.package.models import MyEntity
from application.package.signals import do_stuff

class MyEntityTest(TestCase):
    def setUp(self):
        # 断开原信号连接
        pre_save.disconnect(do_stuff, sender=MyEntity)
        # 创建Mock函数并绑定到信号
        self.mock_do_stuff = Mock()
        pre_save.connect(self.mock_do_stuff, sender=MyEntity)

    def tearDown(self):
        # 测试结束后恢复原信号连接
        pre_save.disconnect(self.mock_do_stuff, sender=MyEntity)
        pre_save.connect(do_stuff, sender=MyEntity)

    def test_save_triggers_mock(self):
        # 创建实例触发信号
        MyEntity.objects.create(name="test")
        # 验证Mock函数被调用
        self.mock_do_stuff.assert_called_once()

方案二:Mock外部资源调用(推荐)

如果do_stuff中的外部资源调用是独立的逻辑,可将这部分拆分为单独函数,直接Mock该依赖函数而非信号处理函数本身:
先重构signals.py:

# signals.py
def call_external_service(data):
    # 实际外部资源调用逻辑
    ...

def do_stuff(sender, instance, **kwargs):
    # 其他逻辑
    call_external_service(instance.data)
    ...

然后在测试中Mock这个外部调用函数:

from django.test import TestCase
from unittest.mock import patch
from application.package.models import MyEntity

class MyEntityTest(TestCase):
    @patch("application.package.signals.call_external_service", autospec=True)
    def test_save_mocks_external_call(self, mock_external):
        MyEntity.objects.create(data="test_data")
        # 验证外部调用被Mock拦截
        mock_external.assert_called_once_with("test_data")

方案三:临时禁用所有信号(谨慎使用)

如果你的测试不需要其他信号生效,可以用Django的override_settings临时禁用信号:

from django.test import TestCase, override_settings
from application.package.models import MyEntity

@override_settings(SIGNAL_PROCESSORS=[])
class MyEntityTest(TestCase):
    def test_save_without_signals(self):
        # 创建实例时不会触发任何信号处理函数
        entity = MyEntity.objects.create(name="test")
        # 验证逻辑
        ...

注意:这种方式会禁用所有信号处理器,可能影响其他测试逻辑,需根据场景使用。

内容的提问来源于stack exchange,提问作者GarlicBread

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.02 23:52:43