正确的方法来测试Django信号

bx2*_*bx2 28 python django tdd unit-testing

我正在尝试测试发送的信号,它是provide_args.contact_question_create表单提交后,在视图内触发信号.

我的TestCase是这样的:

    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')
Run Code Online (Sandbox Code Playgroud)

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

Jos*_*iño 27

最简单的方法来做你在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.
Run Code Online (Sandbox Code Playgroud)

然后,您刚刚测试了您的信号是否被正确触发.

  • 我认为更准确的说法不仅是调用事实,而且还使用mock.assert_called_once_with来调用参数 (4认同)
  • 嗨José,我无法让它工作 - 我认为它可能与dispatch_uid有关.我最终使用'with patch('xyz')作为mocked_handler'方法. (2认同)

bbe*_*ort 13

我有一个替代建议使用该mock库,它现在是unittest.mockPython 3中标准库的一部分(如果你使用的是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")
Run Code Online (Sandbox Code Playgroud)

该建议的基本部分是模拟接收器,然后测试您的信号是否被发送到该接收器,并且只调用一次.这很好,特别是如果你有自定义信号,或者你已经编写了发送信号的方法,并且你想在单元测试中确保它们被发送.

  • AttributeError: 'function' 对象没有属性 'connect' (2认同)

jpi*_*pic 6

你需要:

  • 声明信号与正确的参数emited ,
  • 的特定次数,
  • 以适当的顺序.

您可以使用mock_django app,它为信号提供模拟.

例:

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),
        ])
Run Code Online (Sandbox Code Playgroud)


bx2*_*bx2 5

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

    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')
Run Code Online (Sandbox Code Playgroud)

  • 如果信号发出两次或更多次,这不会失败。 (4认同)

dan*_*ana 5

这样做的目的不是测试基础的信号机制,而是一项重要的单元测试,以确保您的方法应该发出的任何信号实际上都带有适当的参数发出。在这种情况下,由于它是内部django信号,因此似乎有点琐碎,但是可以想象一下,如果您编写的是发出自定义信号的方法。