如何在使用带有静音信号的factory_boy时测试信号

时间:2017-07-26 08:06:26

标签: django factory-boy

我正在使用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)

当我使用工厂类创建模型实例时,如何测试信号是否有效?

1 个答案:

答案 0 :(得分:4)

直接问题的一些解决方案。紧随其后。

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例如,与覆盖和扩展{{基本上相同 1}}方法。我会让你研究一下它是否适用于你的用例。

肯定会三思而后行。

更安全的方法是“静音”/模拟接收器/副作用,而不是发送者

第三方软件包经常使用默认的Django模型信号。将那些隐藏起来可以隐藏起来,以便跟踪因包内交互而导致的错误。

定义和调用(然后在需要时静音)你自己的信号更好,但通常只是重新发明一个方法调用。 Sentry is a good example of signals being used well in a large codebase.

解决方案A是迄今为止最明确和最安全的解决方案。解决方案B和C,无需添加自己的信号,需要小心谨慎。

我不会说没有完全静音save的用例。它应该是一个例外,并且可能会首先仔细检查需求。