为什么我需要像调用方法一样调用我的模拟对象?

0 投票
1 回答
681 浏览
提问于 2025-04-18 17:56

我看了很多关于Python 3和模拟(mocking)的资料,但还是搞不清楚为什么我需要在mock()上验证断言,而不是在mock上。因为我看到的所有文档都是用后者。

这是我的测试代码 -

from unittest.mock import Mock, patch
from unittest import TestCase, skip
from suds.client import Client, ServiceDefinition
from run import WebService
import unittest

@patch('run.Client')
def test(self, mock):
    service = WebService()
    weather_id = 1234
    weather = service.get_weather(weather_id)
    mock().service.GetWeather.assert_called_once_with(weather_id)

在这个例子中,runWebService所在的地方,而Clientsuds客户端。

当我打印mock.mock_calls时,我看到 -

[
    call('SERVICE_WSDL', proxy={'https': 'PROXY', 'http': 'PROXY'}),
    call().factory.create('AuthHeader'),
    call().set_options(soapheaders=<MagicMock name='Client().factory.create()' id='37553264'>),
    call().service.GetWeather(1234, '', '')
]

请注意,我的测试是通过的。我只是想知道我漏掉了什么,这样我可以更好地理解Python中的模拟。

1 个回答

2

首先,我们来给这个变量换个名字,因为它其实是一个模拟的 run.Client 实例:

@patch('run.Client')
def test(self, mock_client):
    # ...
    mock_client().service.GetWeather.assert_called_once_with(weather_id)

你在 run 里面创建了一个 Client 的实例,并且在你的代码中使用了它。但在这个测试中,你并没有真正模拟这个实例,而是模拟了这个类(run.Client 被修改了)。

所以我们有一个模拟的类,而你是通过这个类来创建一个实例。这个实例就是你的代码实际使用的。这意味着你真正想要的是访问这个类构造函数的返回值:

mock_client.return_value.service.GetWeather.assert_called_once_with(weather_id)

这和你的代码是一样的,只是没有调用 mock_client()。你当前的测试代码做的事情类似:调用模拟的类,看看你得到什么实例,然后对这个实例进行验证。然而,mock 库已经提供了 return_value 属性来实现这一点。

撰写回答