将 factory_boy 与静音信号一起使用时如何测试信号
How to test signals when using factory_boy with muted signals
我正在使用 factory_boy 包和 DjangoModelFactory
生成带有静音信号的工厂模型
@factory.django.mute_signals(signals.post_save)
class SomeModelTargetFactory(DjangoModelFactory):
name = factory.Sequence(lambda x: "Name #{}".format(x))
...
我有一个 post_save
信号连接到模型:
def send_notification(sender, instance, created, **kwargs):
if created:
send_email(...)
post_save.connect(send_notification, SomeModel)
当我使用工厂创建模型实例时,如何测试信号是否正常工作class?
直接问题的一些解决方案。其次是警告。
A) 没有关闭信号,mock 副作用
@mock.patch('send_email')
def test_mocking_signal_side_effects(self, mocked_send_email):
my_obj = SomeModelTargetFactory()
# mocked version of send_email was called
self.assertEqual(mocked_send_email.call_count, 1)
my_obj.foo = 'bar'
my_obj.save()
# didn't call send_email again
self.assertEqual(mocked_send_email.call_count, 1)
注意:在 3.3
中加入标准库之前,mock 是单独的包
B) Use as context manager 这样您就可以在测试中有选择地禁用
这将使信号默认打开,但您可以有选择地禁用:
def test_without_signals(self):
with factory.django.mute_signals(signals.post_save):
my_obj = SomeModelTargetFactory()
# ... perform actions w/o signals and assert ...
C) 静音信号和基础工厂的扩展版本
class SomeModelTargetFactory(DjangoModelFactory):
name = factory.Sequence(lambda x: "Name #{}".format(x))
# ...
@factory.django.mute_signals(signals.post_save)
class SomeModelTargetFactoryNoSignals(SomeModelTargetFactory):
pass
我从未尝试过这个,但它似乎应该有效。此外,如果您只需要对象进行不需要持久性的快速单元测试,也许 FactoryBoy's BUILD strategy 是一个可行的选择。
注意:屏蔽信号,尤其是 post_save 可以隐藏讨厌的错误
关于在您自己的代码中使用信号如何产生一种错误的解耦感(post_save
例如,本质上与覆盖信号相同并扩展 save
方法。我会让您研究一下它是否适用于您的用例。
在将其设为默认设置时肯定会三思而后行。
更安全的方法是"mute"/模拟receiver/side效果,而不是发送者。
第三方包经常使用默认的 Django 模型信号。由于包内交互,将它们静音可能很难追踪到错误。
定义和调用(然后在需要时静音)您自己的 信号更好,但通常只是重新发明一个方法调用。 Sentry is a good example of signals being used well in a large codebase.
解决方案 A 是迄今为止最明确和安全的。解决方案 B 和 C,不添加您自己的信号需要小心和注意。
我不会说没有完全静音 post_save
的用例。它应该是一个例外和警告,也许首先要仔细检查需求。
我正在使用 factory_boy 包和 DjangoModelFactory
生成带有静音信号的工厂模型
@factory.django.mute_signals(signals.post_save)
class SomeModelTargetFactory(DjangoModelFactory):
name = factory.Sequence(lambda x: "Name #{}".format(x))
...
我有一个 post_save
信号连接到模型:
def send_notification(sender, instance, created, **kwargs):
if created:
send_email(...)
post_save.connect(send_notification, SomeModel)
当我使用工厂创建模型实例时,如何测试信号是否正常工作class?
直接问题的一些解决方案。其次是警告。
A) 没有关闭信号,mock 副作用
@mock.patch('send_email')
def test_mocking_signal_side_effects(self, mocked_send_email):
my_obj = SomeModelTargetFactory()
# mocked version of send_email was called
self.assertEqual(mocked_send_email.call_count, 1)
my_obj.foo = 'bar'
my_obj.save()
# didn't call send_email again
self.assertEqual(mocked_send_email.call_count, 1)
注意:在 3.3
中加入标准库之前,mock 是单独的包B) Use as context manager 这样您就可以在测试中有选择地禁用
这将使信号默认打开,但您可以有选择地禁用:
def test_without_signals(self):
with factory.django.mute_signals(signals.post_save):
my_obj = SomeModelTargetFactory()
# ... perform actions w/o signals and assert ...
C) 静音信号和基础工厂的扩展版本
class SomeModelTargetFactory(DjangoModelFactory):
name = factory.Sequence(lambda x: "Name #{}".format(x))
# ...
@factory.django.mute_signals(signals.post_save)
class SomeModelTargetFactoryNoSignals(SomeModelTargetFactory):
pass
我从未尝试过这个,但它似乎应该有效。此外,如果您只需要对象进行不需要持久性的快速单元测试,也许 FactoryBoy's BUILD strategy 是一个可行的选择。
注意:屏蔽信号,尤其是 post_save 可以隐藏讨厌的错误
关于在您自己的代码中使用信号如何产生一种错误的解耦感(post_save
例如,本质上与覆盖信号相同并扩展 save
方法。我会让您研究一下它是否适用于您的用例。
在将其设为默认设置时肯定会三思而后行。
更安全的方法是"mute"/模拟receiver/side效果,而不是发送者。
第三方包经常使用默认的 Django 模型信号。由于包内交互,将它们静音可能很难追踪到错误。
定义和调用(然后在需要时静音)您自己的 信号更好,但通常只是重新发明一个方法调用。 Sentry is a good example of signals being used well in a large codebase.
解决方案 A 是迄今为止最明确和安全的。解决方案 B 和 C,不添加您自己的信号需要小心和注意。
我不会说没有完全静音 post_save
的用例。它应该是一个例外和警告,也许首先要仔细检查需求。