Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/python/302.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181

Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/unit-testing/4.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181

Warning: file_get_contents(/data/phpspider/zhask/data//catemap/8/variables/2.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Python 模拟';s autospec将错误的参数注入被调用函数_Python_Unit Testing_Mocking_Python Mock - Fatal编程技术网

Python 模拟';s autospec将错误的参数注入被调用函数

Python 模拟';s autospec将错误的参数注入被调用函数,python,unit-testing,mocking,python-mock,Python,Unit Testing,Mocking,Python Mock,我的理解是,autospec以这里使用的最简单的形式,将根据所提供的参数检查被模拟的函数的签名。其目的是在它们不匹配时引发错误。在下面的代码中,它似乎注入了一个额外的参数——对象本身。为什么使用模拟模块的autospec会导致此处显示的意外行为? 对于这个问题,我在模块simplebutton中创建了一个简化版本。当它作为主模块运行时,会打印一行“这不是开玩笑的” #module simplebutton import sys class _Dialog2: def callback

我的理解是,
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
包含两个测试,这两个测试都有效。两者都模拟
回调
函数。但是,第二个测试包括
autospec=True

    @unittest.mock.patch('simplebutton._Dialog2.callback',
                         name='callback', autospec=True)
在该测试中,应该不使用参数调用的回调函数必须使用
dialog
参数调用,否则测试失败

Edit:每个人都知道,您不是通过
method(instance)
调用方法,而是通过
instance.method()
调用方法。那是我的错误。这里它需要是
instance1.method('instance2')
,其中
instance1
是模拟,而
instance2
是包含模拟方法的对象。感谢米歇尔·达米科的帮助

测试套件如下:

#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
:它与所有其他绑定方法一样,将由self作为第一个参数调用。为了使示例更清晰,我对其进行了更改,以便更好地解释
autospec=True
patch参数的行为

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.\u Dialog2.callback()
作为未绑定方法显式调用
\u Dialog2.callback()
,其行为与
dialog.callback()
完全相同

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


我希望现在清楚地知道当您使用
autospec=True
参数时会发生什么以及您应该期望什么。

我发现在第二次测试中出现“dialog”并不是一个错误。如果不是写“在这个测试中,回调函数应该不带参数调用…”,而是写“在这个测试中,回调方法应该不带参数调用,除了self…”,我的错误是显而易见的。你们的讨论让我明白了这一点,所以我把它作为一个公认的答案。顺便说一句,我使用的是Python 3,它省去了未绑定的方法。@lemi57sss我所说的未绑定和绑定方法只是为了标记对象方法(携带
self
引用并在调用时使用)和没有自引用的函数的区别。我的示例在Python3和Python2.7中工作,并解释了相同的行为。如果a写的不清楚,我道歉。。。英语不是我的强项。
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)])