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

#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是模拟,而instance2是包含模拟方法的对象。感谢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对象的绑定方法。

当在autospec=True装饰器中使用@patch时,它将用新的绑定方法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)])


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

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

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

关于python - Mock的autospec将错误的参数注入(inject)到调用的函数中,我们在Stack Overflow上找到一个类似的问题:https://stackoverflow.com/questions/27878750/

10-12 19:57