我相信正确的修补方法是模拟 Python 类,然后在修补的类上调用
.return_value
但我不确定为什么。假设我有一些代码:
class.py:
class SomeClass:
def __init__(self, some_value):
self.some_value = some_value
def add_to_value(self, add_value):
self.some_value = self.some_value + add_value
function_thing.py:
from my_pkg.class import SomeClass
def some_function():
some_class_instance = SomeClass(5)
some_class_instance.add_to_value(6)
test_function_thing.py:
from my_pkg.function_thing import some_function
class TestSomeFunction(TestCase)
# This works, and it knows the function was called with 6
@patch("my_pkg.function_thing.SomeClass")
def test_some_function_01(self, mock_some_class):
instance_mock = mock_some_class.return_value
some_function()
instance_mock.add_to_value.assert_called_with(6)
# This does not work.
@patch("my_pkg.function_thing.SomeClass.add_to_value")
def test_some_function_02(self, mock_add_to_value):
some_function()
# This succeeds.
mock_add_to_value.assert_called_once()
# This fails, saying it wasn't called with 6 but some object
mock_add_to_value.assert_called_with(6)
看来,为了让模拟正确理解类是如何被调用的,我们不能模拟整个函数。但我们可以断言该函数已被调用。我认为断言该函数被调用将会失败以及它的调用方式,或者两者都会成功。感谢您提供任何信息!
那是因为
SomeClass.add_to_value
有两个参数:self
和 add_value
。
def add_to_value(self, add_value):
尽管 Python 语法可以让你在调用时神奇地去掉
self
参数,但就 Python 而言,它仍然存在。因此,您断言该方法是使用参数元组 (6,)
调用的,而实际上它是使用参数元组 (whatever, 6)
调用的,其中 whatever
是由 some_function
生成的实例。
现在,我们可以继续嘲笑,就像你在例子中所做的那样。但我们或许应该停下来。如果您发现需要模拟一半的代码库来测试一个函数,那么这是一个很好的指标,表明该函数正在做出大量假设,并且应该与其他所有内容分离。在这种情况下,您可以将 SomeClass
实例传递给
some_function
def some_function(some_class_instance=None):
if some_class_instance is None:
some_class_instance = SomeClass(5)
some_class_instance.add_to_value(6)
现在现有的调用者仍然获得“不错的”默认值,但测试(和其他用户)可以使用他们选择的任何实例来调用该函数。
您还可以在工厂级别注入,将工厂传递给一个类。
def some_function(class_to_instantiate=SomeClass):
some_class_instance = class_to_instantiate(5)
some_class_instance.add_to_value(6)
现在您可以将
class_to_instantiate
参数更改为您可以完全控制的类以对其进行测试。