Mock类@property属性返回MagicMock而非预期值及合并Patch问询
Mock Python @property 属性常见问题与解决方案
问题1:Mock @property 返回MagicMock对象而非预期值
原因分析
- 实例创建时机错误:你先通过fixture创建了
ClientApi实例,再对类的@property属性进行patch。由于@property是类级别的描述符,实例在创建时已经绑定了原有的取值逻辑,后续patch类属性无法影响已存在的实例。 - Mock方式不对:直接patch类的
@property属性后,得到的MagicMock对象默认会在被访问(触发__get__方法)时返回自身,而非你设置的return_value——return_value是给可调用对象(如函数)使用的,不适用于描述符的__get__调用逻辑。
修正后的测试代码
有两种可行方案:
方案1:先patch再创建实例
class TestClientApi: def test_create_path(self): with patch("client_api.ClientApi.date_now") as mock_date_now, \ patch("client_api.ClientApi.time_now") as mock_time_now: # 给Mock的__get__方法设置返回值,模拟@property的取值逻辑 mock_date_now.__get__ = lambda *args: "2023-11-28" mock_time_now.__get__ = lambda *args: "00:00:00" # 在patch生效后创建实例 client = ClientApi() # 注意:create_path里是time_now在前,date_now在后,预期路径要对应 expected_path = "client_api_00:00:00T2023-11-28.json" assert expected_path == client.create_path()
方案2:使用PropertyMock(推荐)
unittest.mock提供了PropertyMock专门用于mock@property,可以直接设置返回值:
from unittest.mock import patch, PropertyMock class TestClientApi: def test_create_path(self): with patch("client_api.ClientApi.date_now", new_callable=PropertyMock) as mock_date_now, \ patch("client_api.ClientApi.time_now", new_callable=PropertyMock) as mock_time_now: mock_date_now.return_value = "2023-11-28" mock_time_now.return_value = "00:00:00" client = ClientApi() expected_path = "client_api_00:00:00T2023-11-28.json" assert expected_path == client.create_path()
问题2:合并多个@property的Patch写法
你尝试直接patch整个ClientApi类的写法无效,原因是:
- 已存在的实例(
setup_class)不受后续类patch的影响; - 直接patch整个类后,
mock_client_api.return_value是类的mock实例,但你没有用这个mock实例来测试,仍然用了原来的真实实例。
可行的合并方案
方案1:使用patch.multiple批量patch属性
这是最简洁的方式,配合PropertyMock可以一次性mock多个@property:
from unittest.mock import patch, PropertyMock class TestClientApi: def test_create_path(self): with patch.multiple("client_api.ClientApi", date_now=PropertyMock(return_value="2023-11-28"), time_now=PropertyMock(return_value="00:00:00")): client = ClientApi() expected_path = "client_api_00:00:00T2023-11-28.json" assert expected_path == client.create_path()
方案2:patch整个类并使用mock实例
如果需要mock整个类的其他方法,也可以用这种方式,但要注意使用mock生成的实例:
from unittest.mock import patch class TestClientApi: def test_create_path(self): with patch("client_api.ClientApi") as mock_client_cls: # 获取mock实例 mock_client = mock_client_cls.return_value # 给mock实例的@property设置返回值 type(mock_client).date_now = PropertyMock(return_value="2023-11-28") type(mock_client).time_now = PropertyMock(return_value="00:00:00") # 调用mock实例的方法 expected_path = "client_api_00:00:00T2023-11-28.json" assert expected_path == mock_client.create_path()
内容的提问来源于stack exchange,提问作者HouKaide
相关产品推荐
相关产品推荐

