2010-09-28 48 views
23

我想测试发送的信号,它是offering_args。在表单提交后立即在contact_question_create视图内触发的信号。正确的方式来测试Django信号

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

这是测试这个信号的正确方法?任何更好的想法?

+0

相关:http://stackoverflow.com/questions/13112302/how-do-i-mock-a-django-signal-handler – 2014-06-13 20:41:13

回答

3

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

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

这将不会失败,如果一个信号是两次或更多的emited。 – jpic 2012-10-06 10:47:56

1

为什么你测试你的框架? Django已经为信号调度器进行了单元测试。如果你不相信你的框架是好的,那就把单元测试附加到你的测试运行器上。

+3

请注意,他并未测试Django框架如何表示逻辑行为,但实际测试他的代码何时以及如何触发Django信号。 – 2015-05-12 17:29:21

4

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

6

您需要:

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

您可以使用mock_django应用程序,它提供了mock for signals

例子:

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

请注意,版本1.6.5的mock_django不支持Django 1.5或更高版本。 (在撰写本文时,1.6.5是最新的标记版本,并且是PyPI中的版本。) – 2013-12-20 09:12:27

+1

@jpic你能否请求添加“来自模拟导入电话”的行,因为它不明显? – syabro 2014-12-26 05:31:05

+1

我已经发布了使用模拟库而不是mock_django的更新。见下文。 – bbengfort 2015-02-16 13:15:00

11

我必须使用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) 

建议的重要组成部分是嘲笑接收器,然后测试您的信号是否被发送到接收器,并呼吁只有一次。这很好,特别是如果你有自定义信号,或者你已经编写了发送信号的方法,并且你希望在单元测试中确保它们正在发送。

+0

AttributeError:'function'对象没有属性'connect' – chefarov 2017-06-27 20:02:51

20

最简单的做法是你在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. 

有了这样的,你只是测试你的信号是正确的触发。

+2

您好José,我无法得到它的工作 - 我认为这可能是与dispatch_uid有关。我最终使用了'with patch('xyz')作为mocked_handler'方法。 – stephendwolff 2015-10-22 05:44:34

+2

我认为更准确的是不仅会调用事实,而且会调用参数,使用mock.assert_called_once_with – 2016-02-17 09:49:04