问题描述
我有一个通过装饰器连接的 signal_handler,就像这个非常简单的:
I have a signal_handler connected through a decorator, something like this very simple one:
@receiver(post_save, sender=User, dispatch_uid='myfile.signal_handler_post_save_user') def signal_handler_post_save_user(sender, *args, **kwargs): # do stuff
我想做的是用模拟库 http://www.voidspace 模拟它.org.uk/python/mock/ 在测试中,检查 django 调用它的次数.我现在的代码是这样的:
What I want to do is to mock it with the mock library http://www.voidspace.org.uk/python/mock/ in a test, to check how many times django calls it. My code at the moment is something like:
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)
这里的问题是即使模拟了原始信号处理程序也会被调用,很可能是因为 @receiver 装饰器在某处存储了信号处理程序的副本,所以我模拟了错误的代码.
The problem here is that the original signal handler is called even if mocked, most likely because the @receiver decorator is storing a copy of the signal handler somewhere, so I'm mocking the wrong code.
所以问题是:我如何模拟我的信号处理程序以使我的测试工作?
So the question: how do I mock my signal handler to make my test work?
请注意,如果我将信号处理程序更改为:
Note that if I change my signal handler to:
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)
我改为模拟 _support_function,一切都按预期工作.
and I mock _support_function instead, everything works as expected.
推荐答案
所以,我最终得到了一种解决方案:模拟信号处理程序只是意味着将模拟本身连接到信号,所以这正是我做了:
So, I ended up with a kind-of solution: mocking a signal handler simply means to connect the mock itself to the signal, so this exactly is what I did:
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
请注意,mock.patch 中的 autospec=True 是必需的,以使 post_save.connect 能够在 上正确工作MagicMock,否则django会抛出一些异常,连接会失败.
Notice that autospec=True in mock.patch is required in order to make post_save.connect to correctly work on a MagicMock, otherwise django will raise some exceptions and the connection will fail.