能否通过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
相关产品推荐
相关产品推荐

