测试 Django 信号的正确方法

问题描述 投票:0回答:7

我正在尝试测试发送的信号,它正在提供_args。表单提交后,信号在

contact_question_create
视图内触发。

我的测试用例是这样的:

def test_form_should_post_proper_data_via_signal(self):
    form_data = {'name': 'Jan Nowak'}
    signals.question_posted.send(sender='test', form_data=form_data)

    @receiver(signals.question_posted, sender='test')
    def question_posted_listener(sender, form_data):
        self.name = form_data['name']

    eq_(self.name, 'Jan Nowak')

这是测试该信号的正确方法吗?还有更好的想法吗?

python django unit-testing tdd
7个回答
41
投票

完成您在 2015 年提出的要求的最简单方法:

from unittest.mock import patch

@patch('full.path.to.signals.question_posted.send')
def test_question_posted_signal_triggered(self, mock):
    form = YourForm()
    form.cleaned_data = {'name': 'Jan Nowak'}
    form.save()

    # Check that your signal was called.
    self.assertTrue(mock.called)

    # Check that your signal was called only once.
    self.assertEqual(mock.call_count, 1)

    # Do whatever else, like actually checking if your signal logic did well.

这样,您刚刚测试了信号是否已正确触发。


24
投票

我有一个使用

mock
库的替代建议,该库现在是 Python 3 中
unittest.mock
标准库的一部分(如果您使用的是 Python 2,则必须
pip install mock
)。

try:
    from unittest.mock import MagicMock
except ImportError:
    from mock import MagicMock

def test_form_should_post_proper_data_via_signal(self):
    """
    Assert signal is sent with proper arguments
    """ 

    # Create handler
    handler = MagicMock()
    signals.question_posted.connect(handler, sender='test')

    # Post the form or do what it takes to send the signal
    signals.question_posted.send(sender='test', form_data=form_data)

    # Assert the signal was called only once with the args
    handler.assert_called_once_with(signal=signals.question_posted, form_data=form_data, sender="test")

该建议的核心部分是模拟接收器,然后测试您的信号是否发送到该接收器,并且仅调用一次。这很棒,特别是如果您有自定义信号,或者您已经编写了发送信号的方法并且您希望确保在单元测试中发送信号。


10
投票

我已经自己解决了这个问题。我认为最好的解决方案如下:

    def test_form_should_post_proper_data_via_signal(self):
        # define the local listener
        def question_posted_listener(sender, form_data, **kwargs):
            self.name = form_data['name']

        # prepare fake data
        form_data = {'name': 'Jan Nowak'}

        # connect & send the signal
        signals.question_posted.connect(question_posted_listener, sender='test')
        signals.question_posted.send(sender='test', form_data=form_data)

        # check results
        eq_(self.name, 'Jan Nowak')

8
投票

您需要:

  • 断言已使用适当的参数发出信号
  • 特定次数,
  • 按适当的顺序。

您可以使用mock_django应用程序,它提供了信号模拟

示例:

from mock import call


def test_install_dependency(self):
    with mock_signal_receiver(post_app_install) as install_receiver:
        self.env.install(self.music_app)
        self.assertEqual(install_receiver.call_args_list, [
            call(signal=post_app_install, sender=self.env,
                app=self.ukulele_app),
            call(signal=post_app_install, sender=self.env,
                app=self.music_app),
        ])

7
投票

这样做的目的不是测试底层的信号机制,而是一个重要的单元测试,以确保您的方法应该发出的任何信号实际上都是使用正确的参数发出的。在这种情况下,它看起来有点微不足道,因为它是一个内部 django 信号,但想象一下,如果您编写了发出自定义信号的方法。


1
投票

你为什么要测试你的框架? Django 已经有信号调度程序的单元测试。如果您不相信您的框架没问题,只需将其单元测试附加到您的测试运行程序即可。


1
投票

就我而言,我不会测试信号是否已发送。我会测试信号处理的预期效果。

在我的用例中,当更新 Order.qte_shipped 时,信号用于更新 Produit.qte 属性。 (例如,当我们填写订单时,我希望从该订单的相应产品中减去给定产品的 qte)。

因此我在 signal.py 中做了类似的事情:

@receiver(post_save, sender='orders.Order')
@disable_for_loaddata
def quantity_adjust_order(sender, **kwargs):
    # retrieve the corresponding product for that order
    # subtract Order.qte_shipped from Produit.qte
    # save the updated Produit

我实际测试的是,当我发货订单时,Produit.qte 是否正确更新。我不测试信号是否有效;这只是可以解释为什么

test_order_ship_updates_product()
失败的原因之一。

我有点同意@Piotr Czapla 所说的;您正在尝试测试该框架。而是测试代码上的效果。

© www.soinside.com 2019 - 2024. All rights reserved.