Mock 的 autospec 将错误的参数注入到被调用的函数中
Mock's autospec injects a wrong argument into a called function
我的理解是,此处使用的最简单形式的 autospec
将根据提供的参数检查正在模拟的函数的签名。其目的是在它们不匹配时引发错误。在下面的代码中,它似乎注入了一个额外的参数——对象本身。为什么使用 mock 模块的 autospec
会导致此处显示的意外行为?
对于这个问题,我在模块 simplebutton
中创建了一个简化版本。当它是 运行 作为主模块时,打印行 "It's no joke,"。
#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
。
@unittest.mock.patch('simplebutton._Dialog2.callback',
name='callback', autospec=True)
在这个测试中,应该不带参数调用的回调函数必须用 dialog
的参数调用,否则测试失败。
编辑: 每个人都知道你不是通过 method(instance)
调用方法,而是通过instance.method()
。那是我的错误。这里它需要是 instance1.method('instance2')
,其中 instance1
是 mock,instance2
是包含 mock 方法的对象。感谢 Michele d'Amico 为此。
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
补丁将对象(在您的情况下为 方法 )替换为具有相同原始对象签名的模拟。此外,生成的模拟不能 扩展 :尝试访问不在原始定义中(或 MagicMock()
中)的属性或方法将引发异常。
在第一种情况下(没有 autospec=True
),您正在用无界方法修补绑定方法。当你调用你的补丁方法时,mock_callback
被作为一个函数而不是作为 dialog
对象的绑定方法被调用。
当你在 @patch
装饰器中使用 autospec=True
时,它用一个新的绑定方法替换原来的 mock_callback
:这就像所有其他绑定方法一样,将被自己调用为第一个参数。为了让示例更清楚,我更改了它以更好地解释 autospec=True
补丁参数的行为。
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()
完全相同。
在第二个测试中,如果我们尝试像第一个测试一样将其称为未绑定,则会引发异常。此外,如果我们从两个不同的对象调用该方法,我们将发现对同一个 mock 的两个不同调用,我们可以识别它们。
我希望现在您已经清楚使用 autospec=True
参数时会发生什么以及您应该期待什么。
我的理解是,此处使用的最简单形式的 autospec
将根据提供的参数检查正在模拟的函数的签名。其目的是在它们不匹配时引发错误。在下面的代码中,它似乎注入了一个额外的参数——对象本身。为什么使用 mock 模块的 autospec
会导致此处显示的意外行为?
对于这个问题,我在模块 simplebutton
中创建了一个简化版本。当它是 运行 作为主模块时,打印行 "It's no joke,"。
#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
。
@unittest.mock.patch('simplebutton._Dialog2.callback',
name='callback', autospec=True)
在这个测试中,应该不带参数调用的回调函数必须用 dialog
的参数调用,否则测试失败。
编辑: 每个人都知道你不是通过 method(instance)
调用方法,而是通过instance.method()
。那是我的错误。这里它需要是 instance1.method('instance2')
,其中 instance1
是 mock,instance2
是包含 mock 方法的对象。感谢 Michele d'Amico 为此。
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
补丁将对象(在您的情况下为 方法 )替换为具有相同原始对象签名的模拟。此外,生成的模拟不能 扩展 :尝试访问不在原始定义中(或 MagicMock()
中)的属性或方法将引发异常。
在第一种情况下(没有 autospec=True
),您正在用无界方法修补绑定方法。当你调用你的补丁方法时,mock_callback
被作为一个函数而不是作为 dialog
对象的绑定方法被调用。
当你在 @patch
装饰器中使用 autospec=True
时,它用一个新的绑定方法替换原来的 mock_callback
:这就像所有其他绑定方法一样,将被自己调用为第一个参数。为了让示例更清楚,我更改了它以更好地解释 autospec=True
补丁参数的行为。
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()
完全相同。
在第二个测试中,如果我们尝试像第一个测试一样将其称为未绑定,则会引发异常。此外,如果我们从两个不同的对象调用该方法,我们将发现对同一个 mock 的两个不同调用,我们可以识别它们。
我希望现在您已经清楚使用 autospec=True
参数时会发生什么以及您应该期待什么。