You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Mock类@property属性返回MagicMock而非预期值及合并Patch问询

Mock Python @property 属性常见问题与解决方案

问题1:Mock @property 返回MagicMock对象而非预期值

原因分析

  1. 实例创建时机错误:你先通过fixture创建了ClientApi实例,再对类的@property属性进行patch。由于@property是类级别的描述符,实例在创建时已经绑定了原有的取值逻辑,后续patch类属性无法影响已存在的实例。
  2. 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类的写法无效,原因是:

  1. 已存在的实例(setup_class)不受后续类patch的影响;
  2. 直接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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.03 09:19:56