我有类似的情况:
class BaseClient(object):
def __init__(self, api_key):
self.api_key = api_key
# Doing some staff.
class ConcreteClient(BaseClient):
def get_some_basic_data(self):
# Doing something.
def calculate(self):
# some staff here
self.get_some_basic_data(param)
# some calculations
然后我想使用 get_some_basic_data
函数的模拟来测试 calculate
函数。
我正在做这样的事情:
import unittest
from my_module import ConcreteClient
def my_fake_data(param):
return [{"key1": "val1"}, {"key2": "val2"}]
class ConcreteClientTest(unittest.TestCase):
def setUp(self):
self.client = Mock(ConcreteClient)
def test_calculate(self):
patch.object(ConcreteClient, 'get_some_basic_data',
return_value=my_fake_data).start()
result = self.client.calculate(42)
但它没有按我的预期工作。正如我所想,self.get_some_basic_data(param)
从 my_fake_data
函数返回我的列表,但看起来它是仍然是一个 Mock 对象,这对我来说是不期望的。
这里出了什么问题?
最佳答案
您在这里面临两个主要问题。引发您当前遇到的问题的主要问题是您实际上是如何进行模拟的。现在,由于您实际上是在为 ConcreteClient
修补对象
,因此您需要确保您仍在使用真实 ConcreteClient,但是模拟测试时要模拟的实例的属性。您实际上可以在文档中看到这个插图。不幸的是,没有确切的行的明确 anchor ,但如果您点击此链接:
https://docs.python.org/3/library/unittest.mock-examples.html
该部分指出:
Where you use patch() to create a mock for you, you can get a reference to the mock using the “as” form of the with statement:
引用代码为:
class ProductionClass:
def method(self):
pass
with patch.object(ProductionClass, 'method') as mock_method:
mock_method.return_value = None
real = ProductionClass()
real.method(1, 2, 3)
mock_method.assert_called_with(1, 2, 3)
这里要注意的关键事项是如何调用所有内容。请注意,该类的真实实例已创建。在您的示例中,当您执行此操作时:
self.client = Mock(ConcreteClient)
您正在创建一个 ConcreteClient 上指定的 Mock
对象。因此,最终这只是一个 Mock
对象,它保存 ConcreteClient
的属性。您实际上不会持有 ConcreteClient
的真实实例。
解决这个问题。只需在修补对象后创建一个真实实例即可。另外,为了让你的生活更轻松,这样你就不必手动启动/停止你的 patch.object,使用上下文管理器,它会为你省去很多麻烦。
最后,你的第二个问题是你的return_value
。您的 return_value
实际上返回了 unused my_fake_data
函数。您实际上需要数据本身,因此它需要是该函数的返回。您可以将数据本身作为您的return_value
。
考虑到这两个更正,您的测试现在应该如下所示:
class ConcreteClientTest(unittest.TestCase):
def test_calculate(self):
with patch.object(ConcreteClient, 'get_some_basic_data',
return_value=[{"key1": "val1"}, {"key2": "val2"}]):
concrete_client = ConcreteClient(Mock())
result = concrete_client.calculate()
self.assertEqual(
result,
[{"key1": "val1"}, {"key2": "val2"}]
)
我冒昧地在 calculate
中实际返回了 get_some_basic_data
的结果,只是为了进行比较。我不确定您的真实代码是什么样的。但最终,上面说明了您应该如何执行此操作的测试结构。
关于Python:单元测试中的模拟,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/44556468/