简体   繁体   English

测试 Django 信号的正确方法

[英]Proper way to test Django signals

I'm trying to test sent signal and it's providing_args.我正在尝试测试发送的信号,它正在提供_args。 Signal triggered inside contact_question_create view just after form submission.表单提交后立即在contact_question_create视图中触发信号。

My TestCase is something like:我的测试用例是这样的:

    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')

Is this the proper way to test this signal?这是测试此信号的正确方法吗? Any better ideas?有更好的主意吗?

Simplest way to do what you asked in 2015: 最简单的方法来做你在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.

And with that, you just tested that your signal was properly triggered. 然后,您刚刚测试了您的信号是否被正确触发。

I have an alternative suggestion using the mock library, which is now part of the unittest.mock standard library in Python 3 (if you're using Python 2, you'll have to pip install mock ). 我有一个替代建议使用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")

The essential part of the suggestion is to mock a receiver, then test whether or not your signal is being sent to that receiver, and called only once. 该建议的基本部分是模拟接收器,然后测试您的信号是否被发送到该接收器,并且只调用一次。 This is great, especially if you have custom signals, or you've written methods that send signals and you want to ensure in your unit tests that they are being sent. 这很好,特别是如果你有自定义信号,或者你已经编写了发送信号的方法,并且你想在单元测试中确保它们被发送。

You need to: 你需要:

  • assert a signal was emited with proper arguments and , 声明信号与正确的参数 emited,
  • a specific number of times and , 的特定次数
  • in appropriate order. 以适当的顺序。

You can use mock_django app which provides a mock for signals . 您可以使用mock_django app,它为信号提供模拟

Example: 例:

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),
        ])

I've resolved the problem by myself. 我自己解决了这个问题。 I think that the best solution is following: 我认为最好的解决方案如下:

    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')

The purpose of this isn't to test the underlying signalling mechanism, but rather is an important unit test to ensure that whatever signal your method is supposed to emit is actually emitted with the proper arguments. 这样做的目的不是测试底层的信号机制,而是一个重要的单元测试,以确保您的方法应该发出的任何信号实际上是用适当的参数发出的。 In this case, it seems a little trivial since its an internal django signal, but imagine if you wrote the method that was emitting a custom signal. 在这种情况下,它似乎有点微不足道,因为它是一个内部django信号,但想象一下,如果你编写了发出自定义信号的方法。

Why do you test your framework? 为什么要测试你的框架? Django already have unit tests for signal dispatcher. Django已经为信号调度员进行了单元测试。 If you don't believe that your framework is fine just attach it unit tests to yours test runner. 如果你不相信你的框架是好的,只需将它的单元测试附加到你的测试运行器上。

For my part, I wouldn't test that the signal is sent.就我而言,我不会测试信号是否已发送。 I would test the intended effect of the signals processing.我会测试信号处理的预期效果。

In my use case, the signals are used to update a Produit.qte attribute when, say, Order.qte_shipped is upated.在我的用例中,信号用于在更新 Order.qte_shipped 时更新 Produit.qte 属性。 (Eg when we fill an order, I want the qte of the given product to be subtracted from the corresponding product for that order). (例如,当我们填写订单时,我希望从该订单的相应产品中减去给定产品的 qte)。

Thus I do something like this in signals.py:因此我在 signals.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

What I actually test is that Produit.qte is correctly updated when I ship an Order.我实际测试的是当我发送订单时 Produit.qte 已正确更新。 I do not test that the signals works;没有测试信号是否有效; that's just one of the things that COULD explain why test_order_ship_updates_product() failed.这只是可以解释为什么test_order_ship_updates_product()失败的原因之一。

I somewhat agree with what @Piotr Czapla said;我有点同意@Piotr Czapla 所说的; you're kind of trying to test the framework.您是在尝试测试框架。 Test the effect on your code instead.而是测试对您的代码的影响。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM