Python函数未使用模拟对象

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路径以提取实际数据。

我想念什么?

Mar*_*ers 5

您做错了三件事:

  • 您正在修补错误的位置。您需要修补yyyy.Client全局名称,因为这就是您导入该名称的方式。

  • 被测代码未调用Client(),它使用不同的方法,因此调用路径不同。

  • 您正在补丁生命周期之外调用被测代码。在with块中调用您的代码。

让我们详细介绍一下:

当你使用from xxxx import Client,你绑定一个新的参考Clientyyyy模块全局到该对象。您要替换该引用,而不是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)