如何修补 "autospec" 设置为 True 的对象?
How to patch objects with "autospec" set to True?
我有以下玩具class:
class MyClass:
def __init__(self, x):
self.x = x
def get_operator(self):
answer = input("Multiply? ")
if answer == "y":
return "multiply"
def multiply(self, y):
if self.get_operator() == "multiply":
return self.x * y
以下测试(使用 pytest)将抛出错误:
def test_multiply_is_called(mocker):
multiply = mocker.patch("package.module.MyClass.multiply", return_value=1, autospec=True)
my_instance = MyClass(2)
my_instance.multiply(3)
multiply.assert_called_once() # no error whatever autospec is equal to (True or False)
multiply.assert_called_with(3) # no error only if autospec=False
TypeError: Can't use 'autospec' with create=True
当试图模拟 Python 内置函数时,它变得更不清楚 input
:
def test_input_is_called_once(mocker):
input = mocker.patch("package.module.input", return_value="y", autospec=True)
my_instance = MyClass(2)
my_instance.get_operator()
input.assert_called_once() # no error only if autospec=False
E AssertionError: expected call not found.
E Expected: multiply(3)
E Actual: multiply(<package.module.MyClass object at 0x0000022A4BEEFD00>, 3)
我知道用 autospec=True
进行模拟是一种推荐的做法,但是,很明显,我对它的工作原理有错误的理解,例如,尽管阅读了 this post and 。
有人可以澄清一下这个主题吗?
这其实是两个不相关的问题。
第一个问题源于这样一个事实,即使用 autospec=False
,调用 my_instance.multiply(3)
只是在没有任何检查的情况下使用参数 3 调用 mock(例如,简单的函数调用)。
但是,如果使用 autospec=True
,则方法 multiply
会像在实际调用中一样绑定到 my_instance
,并作为方法调用,其中 my_instance
作为第一个(self
) 参数。因此,要使其正常工作,您需要:
def test_multiply_is_called(mocker):
multiply = mocker.patch("package.module.MyClass.multiply", return_value=1, autospec=True)
my_instance = MyClass(2)
my_instance.multiply(3)
multiply.assert_called_with(my_instance, 3)
第二种情况不同,我不得不承认我并不完全清楚它是如何与 autospec=False
一起正常工作的。问题是 input
没有绑定到模块,但它是一个全局导入(来自 builtins
),因此它被创建为一个新的模拟对象,而不知道实际的内置对象功能。在这种情况下,autospec
将因为缺少信息而不起作用。这可以通过模拟内置函数来解决:
def test_input_is_called_once(mocker):
input = mocker.patch("builtins.input", return_value="y", autospec=True)
my_instance = MyClass(2)
my_instance.get_operator()
input.assert_called_once()
这适用于 autospec=True
和 autospec=False
。据我了解,使用 autospec=False
它之所以有效,是因为为本地 input
函数创建了一个模拟,然后在调用中实际使用了该模拟。从我的角度来看,模拟 input
的正确方法是模拟 builtins.input
,无论如何。
我有以下玩具class:
class MyClass:
def __init__(self, x):
self.x = x
def get_operator(self):
answer = input("Multiply? ")
if answer == "y":
return "multiply"
def multiply(self, y):
if self.get_operator() == "multiply":
return self.x * y
以下测试(使用 pytest)将抛出错误:
def test_multiply_is_called(mocker):
multiply = mocker.patch("package.module.MyClass.multiply", return_value=1, autospec=True)
my_instance = MyClass(2)
my_instance.multiply(3)
multiply.assert_called_once() # no error whatever autospec is equal to (True or False)
multiply.assert_called_with(3) # no error only if autospec=False
TypeError: Can't use 'autospec' with create=True
当试图模拟 Python 内置函数时,它变得更不清楚 input
:
def test_input_is_called_once(mocker):
input = mocker.patch("package.module.input", return_value="y", autospec=True)
my_instance = MyClass(2)
my_instance.get_operator()
input.assert_called_once() # no error only if autospec=False
E AssertionError: expected call not found.
E Expected: multiply(3)
E Actual: multiply(<package.module.MyClass object at 0x0000022A4BEEFD00>, 3)
我知道用 autospec=True
进行模拟是一种推荐的做法,但是,很明显,我对它的工作原理有错误的理解,例如,尽管阅读了 this post and
有人可以澄清一下这个主题吗?
这其实是两个不相关的问题。
第一个问题源于这样一个事实,即使用 autospec=False
,调用 my_instance.multiply(3)
只是在没有任何检查的情况下使用参数 3 调用 mock(例如,简单的函数调用)。
但是,如果使用 autospec=True
,则方法 multiply
会像在实际调用中一样绑定到 my_instance
,并作为方法调用,其中 my_instance
作为第一个(self
) 参数。因此,要使其正常工作,您需要:
def test_multiply_is_called(mocker):
multiply = mocker.patch("package.module.MyClass.multiply", return_value=1, autospec=True)
my_instance = MyClass(2)
my_instance.multiply(3)
multiply.assert_called_with(my_instance, 3)
第二种情况不同,我不得不承认我并不完全清楚它是如何与 autospec=False
一起正常工作的。问题是 input
没有绑定到模块,但它是一个全局导入(来自 builtins
),因此它被创建为一个新的模拟对象,而不知道实际的内置对象功能。在这种情况下,autospec
将因为缺少信息而不起作用。这可以通过模拟内置函数来解决:
def test_input_is_called_once(mocker):
input = mocker.patch("builtins.input", return_value="y", autospec=True)
my_instance = MyClass(2)
my_instance.get_operator()
input.assert_called_once()
这适用于 autospec=True
和 autospec=False
。据我了解,使用 autospec=False
它之所以有效,是因为为本地 input
函数创建了一个模拟,然后在调用中实际使用了该模拟。从我的角度来看,模拟 input
的正确方法是模拟 builtins.input
,无论如何。