Sus*_*y11 4 python unit-testing mocking
来自PHP背景,在编写Python单元测试时遇到了以下问题:
我有使用客户端对象的foo函数,以便从其他一些API获得响应:
from xxxx import Client
def foo (some_id, token):
path = 'some/api/path'
with Client.get_client_auth(token) as client:
response = client.get(path,params).json()
results = list(response.keys())
.............
Run Code Online (Sandbox Code Playgroud)
为此,我在另一个python文件中创建了以下单元测试。
from yyyy import foo
class SomethingTestCase(param1, param2):
def test_foo(self):
response = [1,2,3]
with patch('xxxx.Client') as MockClient:
instance = MockClient.return_value
instance.get.return_value = response
result = foo(1,self.token)
self.assertEqual(response,result)
Run Code Online (Sandbox Code Playgroud)
我不明白为什么foo不使用模拟的[1,2,3]列表,而是尝试连接到实际的API路径以提取实际数据。
我想念什么?
您做错了三件事:
您正在修补错误的位置。您需要修补yyyy.Client全局名称,因为这就是您导入该名称的方式。
被测代码未调用Client(),它使用不同的方法,因此调用路径不同。
您正在补丁生命周期之外调用被测代码。在with块中调用您的代码。
让我们详细介绍一下:
当你使用from xxxx import Client,你绑定一个新的参考Client在yyyy模块全局到该对象。您要替换该引用,而不是xxxx.Client。毕竟,被测代码Client在其自己的模块中以全局方式访问。看到哪里修补部分的的unittest.mock文档。
您没有调用 Client代码。您正在其上使用类方法(.get_client_auth())。然后,您还将返回值用作上下文管理器,因此分配给上下文管理器上方法client的返回值__enter__:
with Client.get_client_auth(token) as client:
Run Code Online (Sandbox Code Playgroud)
您需要模拟该方法链:
with patch('yyyy.Client') as MockClient:
context_manager = MockClient.get_client_auth.return_value
mock_client = context_manager.__enter__.return_value
mock_client.get.return_value = response
result = foo(1,self.token)
Run Code Online (Sandbox Code Playgroud)
您需要在with块中调用被测代码,因为只有在该块中才会对代码进行修补。该with语句将patch(...)结果用作上下文管理器。输入该块后,实际上会将修补程序应用于模块,而当退出该块时,将再次删除该修补程序。
最后但并非最不重要的一点是,当尝试调试这种情况时,您可以打印出Mock.mock_calls属性;这应该告诉您实际在对象上调用了什么。没有拨打电话?然后,您尚未修补正确的位置,忘记了启动修补程序,或者在修补程序不再存在时调用了被测代码。
但是,如果您的补丁正确应用,则将MockClient.mock_calls类似于:
[call.get_client_auth('token'),
call.get_client_auth().__enter__(),
call.get_client_auth().__enter__().get('some/api/path', {'param': 'value'}),
call.get_client_auth().__exit__(None, None, None)]
Run Code Online (Sandbox Code Playgroud)
| 归档时间: |
|
| 查看次数: |
1332 次 |
| 最近记录: |