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

Python单元测试:如何在MagicMock的side_effect中更新self属性

问题原因
  • TestSample1能正常运行的核心:你patch的是OAuthSession类的真实实例方法,加上autospec=True后,调用方法时会自动把实例对象作为self参数传入side_effect函数;但真实的OAuthSession实例不存在a/g这类属性,必须手动逐层赋值MagicMock,否则访问深层属性会抛AttributeError,配置繁琐。
  • TestSample2运行失败的核心:你patch了整个OAuthSession类,OAuthSession()返回的本身就是MagicMock实例,不是真实类对象,调用mock的方法时不会自动绑定宿主实例作为self传给side_effect,参数匹配逻辑和真实实例方法不一致。
  • TestSample3报错的核心:直接创建的MagicMock实例的方法是独立mock对象,调用时不会隐式把外层mock实例作为第一个参数传入side_effect,所以你的mocked_refresh_access_token收不到预期的self参数,抛出缺参错误。
最优修正方案(完全满足无需逐层手动赋值的需求)

直接修正TestSample3的写法即可,MagicMock本身就支持自动创建任意层级的子属性,不需要手动给每一层赋值Mock,只需要调整side_effect的参数逻辑,用闭包直接引用外层mock实例即可:

import unittest
from unittest import mock


class OAuthSession():
    def refresh_access_token(self):
        self.access_token = 'refreshed new token'


def target_method(auth):
    if auth.a.b.c.d.e:
        auth.refresh_access_token()
    return auth.access_token


class TestSampleFixed(unittest.TestCase):
    def test_target_method(self):
        oauth_session = mock.MagicMock()
        # 直接赋值深层属性即可,MagicMock会自动创建中间层级的mock对象,无需手动逐层配置
        oauth_session.a.b.c.d.e = True
        oauth_session.g.h.i.j.k = True
        
        # 修正side_effect:mock方法不会自动传self,直接闭包引用外层实例设置属性即可
        def _mock_refresh():
            oauth_session.access_token = 'mocked refreshed token'
        oauth_session.refresh_access_token.side_effect = _mock_refresh

        result = target_method(oauth_session)
        assert result == 'mocked refreshed token'

这个写法不需要patch任何真实类/方法,不需要逐层手动创建中间属性,代码量比TestSample1少一半以上,运行可以直接通过。

其他可选优化(如果要保留原mocked_refresh_access_token函数)

如果你不想改写原来的接收self的mock函数,可以在调用side_effect时手动把实例传进去,用lambda包一层即可:

def mocked_refresh_access_token(self):
    self.access_token = 'mocked refreshed token'

class TestSampleFixed2(unittest.TestCase):
    def test_target_method(self):
        oauth_session = mock.MagicMock()
        oauth_session.a.b.c.d.e = True
        oauth_session.g.h.i.j.k = True
        # 用lambda手动传入self参数
        oauth_session.refresh_access_token.side_effect = lambda: mocked_refresh_access_token(oauth_session)
        
        result = target_method(oauth_session)
        assert result == 'mocked refreshed token'

注意:不要尝试给MagicMock的side_effect直接绑定原带self参数的函数,因为mock方法的调用逻辑和Python原生实例方法的绑定逻辑完全不同,原生实例方法会自动绑定宿主实例作为第一个参数,mock方法不会做这个隐式操作。

内容的提问来源于stack exchange,提问作者Sheile

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.27 21:21:23