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
相关产品推荐
相关产品推荐

