我如何模拟django信号处理程序?

Ste*_*noP 43 python django signals mocking django-signals

我有一个通过装饰器连接的signal_handler,这个非常简单:

@receiver(post_save, sender=User, 
          dispatch_uid='myfile.signal_handler_post_save_user')
def signal_handler_post_save_user(sender, *args, **kwargs):
   # do stuff
Run Code Online (Sandbox Code Playgroud)

我想做的是在测试中使用模拟库http://www.voidspace.org.uk/python/mock/进行模拟,以检查django调用它的次数.我的代码目前是这样的:

def test_cache():
    with mock.patch('myapp.myfile.signal_handler_post_save_user') as mocked_handler:
        # do stuff that will call the post_save of User
    self.assert_equal(mocked_handler.call_count, 1)
Run Code Online (Sandbox Code Playgroud)

这里的问题是即使被模拟也会调用原始的信号处理程序,这很可能是因为@receiver装饰器正在某处存储信号处理程序的副本,所以我在嘲笑错误的代码.

所以问题是:如何模拟我的信号处理程序以使我的测试工作?

请注意,如果我将信号处理程序更改为:

def _support_function(*args, **kwargs):
    # do stuff

@receiver(post_save, sender=User, 
          dispatch_uid='myfile.signal_handler_post_save_user')
def signal_handler_post_save_user(sender, *args, **kwargs):
   _support_function(*args, **kwargs)
Run Code Online (Sandbox Code Playgroud)

而我嘲笑_support_function,一切都按预期工作.

Mei*_*tro 19

可能更好的想法是模拟信号处理程序内部的功能而不是处理程序本身.使用OP的代码:

@receiver(post_save, sender=User, dispatch_uid='myfile.signal_handler_post_save_user')
def signal_handler_post_save_user(sender, *args, **kwargs):
  do_stuff()  # <-- mock this

def do_stuff():
   ... do stuff in here
Run Code Online (Sandbox Code Playgroud)

然后模拟do_stuff:

with mock.patch('myapp.myfile.do_stuff') as mocked_handler:
    self.assert_equal(mocked_handler.call_count, 1)
Run Code Online (Sandbox Code Playgroud)

  • 为什么这是一个更好的主意? (3认同)

Ste*_*noP 18

所以,我最终得到了一种解决方案:模拟信号处理程序只是意味着将模拟本身连接到信号,所以这正是我所做的:

def test_cache():
    with mock.patch('myapp.myfile.signal_handler_post_save_user', autospec=True) as mocked_handler:
        post_save.connect(mocked_handler, sender=User, dispatch_uid='test_cache_mocked_handler')
        # do stuff that will call the post_save of User
    self.assertEquals(mocked_handler.call_count, 1)  # standard django
    # self.assert_equal(mocked_handler.call_count, 1)  # when using django-nose
Run Code Online (Sandbox Code Playgroud)

请注意,autospec=Truemock.patch是为了使需要post_save.connect对一个正常工作MagicMock,否则Django将提高一些例外,连接将失败.

  • 您实际上并没有测试信号,而是最终测试了核心 django 的信号功能,而不是您的自定义代码。如果将 `signal_handler_post_save_user` 替换为测试用例将通过的任何其他函数,那是因为您显式地使该处理函数订阅了 `post_save` 事件,然后再次检查该处理函数是否被触发。这样,您就无法真正确定您编写的并且应该在“post_save”事件上运行的处理程序函数是否被触发。 (4认同)
  • 这取决于您正在使用的测试套件; django默认使用`unittest`,它有`assertEquals`就像你说的那样; 我总是使用`nose`,在我看来,这在许多方面都是优越的,鼻子带有`assert_equal`.在写我的答案时,我从我的生产代码中复制/粘贴,这就是你在那里看到`assert_equal`的原因.我编辑了尊重django默认的答案,谢谢你指出这一点 (2认同)
  • 它有用吗?它对我不起作用:( (2认同)
  • 这对我也不起作用。仍然会调用“原始”处理程序。 (2认同)