本文介绍了Mock的autospec将错误的参数注入到一个被调用的函数中的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我的理解是,此处使用的autospec以其最简单的形式将检查所提供的参数所模拟的函数的签名.其目的是在不匹配时引发错误.在下面的代码中,它似乎注入了一个附加参数-对象本身.为什么使用模拟模块的autospec会导致此处所示的意外行为?对于这个问题,我在模块simplebutton中创建了一个简化版本.当它作为主要模块运行时,将显示这不是开玩笑"行.

My understanding is that autospec in its simplest form as used here will check the signature of the function which is being mocked against the presented arguments. Its purpose is to raise an error if they do not match. In the code below it seems to inject an additional argument - the object itself. Why does the use of the mock module's autospec lead to the unexpected behavior shown here?For this question I've created a simplified version in the module simplebutton. When it's run as the main module the line, "It's no joke," is printed.

#module simplebutton
import sys


class _Dialog2:
    def callback(self):
        print("It's no joke")


def main():
    dialog = _Dialog2()
    dialog.callback()


if __name__ == '__main__':
    sys.exit(main())

测试模块test_simplebutton包含两个测试,这两个测试均有效.两者都模拟callback函数.但是,第二项测试包含autospec=True.

The test module test_simplebutton contains two tests both of which work. Both mock the callback function. The second test, however, includes autospec=True.

    @unittest.mock.patch('simplebutton._Dialog2.callback',
                         name='callback', autospec=True)

在此测试中,应使用参数dialog调用不带参数的回调函数,否则测试将失败.

In this test the callback function which should be called with no arguments has to be called with an argument of dialog otherwise the test fails.

每个人都知道您不是通过method(instance)而是通过instance.method()来调用方法.那是我的错这里它必须是instance1.method('instance2'),其中instance1是模拟,而instance2是包含模拟方法的对象.感谢Michele d'Amico对此.

Everyone knows that you don't call a method by method(instance) but by instance.method(). That was my mistake. Here it needs to be instance1.method('instance2') where instance1 is the mock and instance2 is the object containing the mocked method. Thanks to Michele d'Amico for this.

        mock_callback.assert_called_once_with(dialog)    

测试套件如下:

#module test_simplebutton
import unittest
import unittest.mock

import simplebutton


class Test_Dialog(unittest.TestCase):

    @unittest.mock.patch('simplebutton._Dialog2.callback',
                         name='callback')
    def test_direct_call_to_callback_by_mocking_1(self, mock_callback):
        dialog = simplebutton._Dialog2()
        dialog.callback()
        mock_callback.assert_called_once_with()

    @unittest.mock.patch('simplebutton._Dialog2.callback',
                         name='callback', autospec=True)
    def test_direct_call_to_callback_by_mocking_2(self, mock_callback):
        dialog = simplebutton._Dialog2()
        dialog.callback()
        mock_callback.assert_called_once_with(dialog)

推荐答案

通过autospec=True修补程序用具有相同原始对象签名的模拟对象替换对象(在您的情况下为 method ).此外,生成的模拟不能进行扩展:尝试访问原始定义(或MagicMock()中)以外的属性或方法将引发异常.

By autospec=True patch replace a object (in your case method) by a mock with same original objects's signature. Moreover the resulting mock cannot be extended: try to access to attributes or methods that are not in the original definition (or in MagicMock()) will raise an exception.

在第一种情况下(没有autospec=True),您正在通过无界方法来修补有界方法.调用修补方法时,mock_callback被称为函数而不是dialog对象的绑定方法.

In the first case (without autospec=True) you are patching a bound method by an unbounded one. When you invoked your patched method, mock_callback is called as a function and not as a bound method of dialog object.

@patch装饰器中使用autospec=True时,它将用新的绑定方法mock_callback替换原始的:与其他所有绑定方法一样,它将作为第一个参数自行调用.为了使示例更清晰,我对其进行了更改,以更好地解释autospec=True补丁参数的行为.

When you use autospec=True in @patch decorator it replace the original one by a new bound method mock_callback: that is like all other bound method and will be call by self as first argument. In order to the example more clear I changed it to explain better the behavior of autospec=True patch's parameter.

import unittest
import unittest.mock

import simplebutton

class Test_Dialog(unittest.TestCase):

    @unittest.mock.patch('simplebutton._Dialog2.callback')
    def test_direct_call_to_callback_by_mocking_1(self, mock_callback):
        dialog = simplebutton._Dialog2()
        dialog.callback()
        mock_callback.assert_called_once_with()
        mock_callback.reset_mock()
        simplebutton._Dialog2.callback()
        mock_callback.assert_called_once_with()

    @unittest.mock.patch('simplebutton._Dialog2.callback', autospec=True)
    def test_direct_call_to_callback_by_mocking_2(self, mock_callback):
        dialog = simplebutton._Dialog2()
        dialog.callback()
        mock_callback.assert_called_once_with(dialog)
        self.assertRaises(Exception, simplebutton._Dialog2.callback)

        dialog2 = simplebutton._Dialog2()
        dialog.callback()
        dialog2.callback()
        mock_callback.assert_has_calls([unittest.mock.call(dialog), unittest.mock.call(dialog2)])

在第一个测试中,我们通过simplebutton._Dialog2.callback()作为未绑定方法显式调用了_Dialog2.callback(),其行为与dialog.callback()完全相同.

In the first test we make either an explicit call to _Dialog2.callback() as unbound method by simplebutton._Dialog2.callback() and the behavior is exactly the same of dialog.callback().

在第二个测试中,如果我们像第一个测试一样尝试将其称为未绑定,则会引发异常.而且,如果我们从两个不同的对象调用该方法,则会发现对同一模拟的两个不同的调用,并且可以识别它们.

In the second one if we try to call it as unbound like the first test it will raise an exception. Moreover if we call the method from two different objects we will found two different call to the same mock and we can identify them.

我希望现在清楚使用autospec=True参数时会发生什么以及应该期待什么.

I hope is clear now what happen and what you should expect when you use autospec=True parameter.

这篇关于Mock的autospec将错误的参数注入到一个被调用的函数中的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持!

09-27 05:21