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

如何在Python中仅Mock类的非顶级调用以测试批量执行方法

实现指定方法真实执行、内部子方法Mock的测试方案

核心需求

测试Foo类的apply_all方法时,让apply_all本身真实执行,但其内部调用的所有apply_*(除apply_all外)方法替换为Mock,以便验证这些方法的调用情况。

解决方案一:动态替换实例方法

通过inspect模块筛选目标方法,批量将实例上的apply_*方法替换为Mock,保留apply_all的真实实现:

from unittest.mock import MagicMock
import inspect

class Foo:
    def apply_goal_filter(self):
        print("真实执行goal过滤")
    def apply_gender_filter(self):
        print("真实执行gender过滤")
    def apply_age_filter(self):
        print("真实执行age过滤")
    def apply_all(self):
        self.apply_goal_filter()
        self.apply_gender_filter()
        self.apply_age_filter()

def test_apply_all():
    foo = Foo()
    # 筛选所有以apply_开头、且不是apply_all的方法
    target_methods = [
        name for name, _ in inspect.getmembers(Foo, predicate=inspect.isfunction)
        if name.startswith("apply_") and name != "apply_all"
    ]

    # 批量替换为Mock并缓存
    mock_map = {}
    for method_name in target_methods:
        mock = MagicMock()
        mock_map[method_name] = mock
        setattr(foo, method_name, mock)

    # 真实调用apply_all
    foo.apply_all()

    # 验证每个Mock方法都被调用一次
    for method, mock in mock_map.items():
        mock.assert_called_once()

test_apply_all()

解决方案二:自定义包装类拦截属性访问

通过包装类统一处理实例的属性访问,仅对指定方法放行真实调用,其余apply_*返回Mock:

from unittest.mock import MagicMock

class MockWrapper:
    def __init__(self, instance, keep_real_methods):
        self.instance = instance
        self.keep_real = keep_real_methods
        self.mock_cache = {}

    def __getattr__(self, name):
        # 保留指定方法的真实实现
        if name in self.keep_real:
            return getattr(self.instance, name)
        # 对apply_开头的方法返回Mock(缓存避免重复创建)
        if name.startswith("apply_"):
            if name not in self.mock_cache:
                self.mock_cache[name] = MagicMock()
            return self.mock_cache[name]
        # 其他属性/方法正常转发
        return getattr(self.instance, name)

def test_apply_all_with_wrapper():
    foo = Foo()
    # 包装实例,指定apply_all为真实执行的方法
    wrapped_foo = MockWrapper(foo, keep_real_methods=["apply_all"])

    # 调用真实的apply_all
    wrapped_foo.apply_all()

    # 验证Mock方法的调用情况
    wrapped_foo.mock_cache["apply_goal_filter"].assert_called_once()
    wrapped_foo.mock_cache["apply_gender_filter"].assert_called_once()
    wrapped_foo.mock_cache["apply_age_filter"].assert_called_once()

test_apply_all_with_wrapper()

为什么Mock(wraps=foo)不满足需求

Mock(wraps=foo)会将所有调用转发给原实例,仅记录调用行为,不会替换方法为Mock实现,因此内部的apply_*方法仍会真实执行,无法达到只Mock子方法的目的。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.06 21:01:05