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

能否通过pytest检测函数是否仅被间接调用而未被测试直接调用?

如何用pytest检测函数是否仅被间接调用而未直接测试?

示例

def main(p):
    if foo_a(p):
        return False
    return p**2
def foo_a(p):
    return p % 11 == 0

通过以下测试代码可达到100%测试覆盖率:

import unittest
from script import main

class Foobar(unittest.TestCase):
    def test_main(self):
        self.assertEquals(main(3), 9)

但开发者可能原本希望foo_a的逻辑是p % 2 == 0。

问题

分支覆盖率可一定程度上反映此类问题,但我还希望了解某个函数是否从未被测试用例直接调用(如示例中的main是被直接调用的),仅通过其他函数间接调用(如示例中的foo_a)。

请问这能否通过pytest实现?


当然可以用pytest实现这个需求!这里有几种实用的方法,根据你的场景灵活选择:

方法1:用pytest-mock跟踪调用来源

借助pytest-mock的mock能力,给目标函数套一层追踪逻辑,记录调用它的上下文,就能区分是测试用例直接调用,还是通过其他函数间接调用:

import inspect
import pytest
from script import foo_a, main

def test_foo_a_call_origin(mocker):
    call_records = []

    def track_caller(*args, **kwargs):
        # 获取调用栈的上一层帧信息
        caller_frame = inspect.stack()[1]
        # 判断调用是否来自测试用例(这里假设测试函数以test_开头)
        is_direct_test_call = caller_frame.function.startswith("test_")
        call_records.append(is_direct_test_call)
        # 执行原函数的逻辑
        return args[0] % 11 == 0

    # 替换原foo_a为我们的追踪函数
    mocker.patch("script.foo_a", side_effect=track_caller)

    # 先触发一次间接调用(通过main)
    main(3)
    # 再触发一次直接调用
    foo_a(11)

    # 验证记录:第一次是间接调用(False),第二次是直接调用(True)
    assert call_records == [False, True]

如果需要批量检查多个函数,可以把这个逻辑封装成fixture,自动遍历模块内的函数进行追踪。

方法2:用sys.settrace做全局调用追踪

如果想一次性监控所有函数的调用情况,可以用Python底层的sys.settrace钩子,在测试运行时记录每个函数的调用来源:

import sys
import pytest
from script import foo_a, main

def test_all_function_call_origins():
    call_info = {}

    def trace_handler(frame, event, arg):
        if event == 'call':
            func_name = frame.f_code.co_name
            # 获取调用者的函数名
            caller_frame = frame.f_back
            caller_func = caller_frame.f_code.co_name if caller_frame else "top_level"
            
            # 标记是否来自测试用例
            is_direct_test = caller_func.startswith("test_")
            
            # 初始化函数的调用记录
            if func_name not in call_info:
                call_info[func_name] = {"direct_test_call": False, "indirect_call": False}
            
            if is_direct_test:
                call_info[func_name]["direct_test_call"] = True
            else:
                call_info[func_name]["indirect_call"] = True
        return trace_handler

    # 开启追踪
    sys.settrace(trace_handler)
    try:
        # 运行测试里的逻辑
        main(3)
    finally:
        # 关闭追踪,避免影响其他测试
        sys.settrace(None)

    # 验证结果:main有直接测试调用,foo_a只有间接调用
    assert call_info["main"]["direct_test_call"] is True
    assert call_info["foo_a"]["direct_test_call"] is False
    assert call_info["foo_a"]["indirect_call"] is True

这种方式适合全局批量分析模块内所有函数的调用情况,不需要逐个函数写mock。

总结

pytest结合mock工具或者Python的原生追踪机制,完全能实现你要的需求:单个函数检查用mock更轻量,全局批量检查用sys.settrace更高效。根据你的实际场景选对应的方法就行。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.28 04:00:16