递归函数健壮性关键在于防御性校验、明确终止条件、深度控制与可验证错误传播。需首行校验参数类型与范围,覆盖所有基础情形,空结构设守卫,显式传入并校验递归深度,用mock和parametrize系统测试合法/边界/非法输入。

递归函数要真正可靠、容易验证,关键不在“让它跑通”,而在它面对异常输入、深度嵌套、中间出错时,不崩、不卡、不静默失败,且每一步行为都可观察、可断言。
明确终止条件并防御性校验入口
递归最常崩在起点。不能只写 if n == 0: return 1 就完事,得提前拦住非法输入。
- 所有参数在函数第一行就做类型和范围检查:比如
n是负数、浮点数、None、字符串,都要立刻抛出清晰异常(如ValueError("n must be non-negative integer")),而不是等递归几层后才报错或陷入死循环。 - 终止条件必须覆盖所有基础情形:
factorial(0)和factorial(1)都要单独验证,不能只测factorial(5)就认为对了。 - 对空结构(空列表、空字典、null 节点)设守卫:树遍历中
if !node: return必须出现在最前,否则后续.children访问直接 TypeError 中断整条链。
控制递归深度,避免栈溢出
默认递归限制(Python 约 1000 层)不是安全边界,而是最后防线。
- 显式传入
depth=0和max_depth=10参数,每次递归前判断if depth >= max_depth: raise RecursionError(...)。 - 深度值要业务合理:权限树、配置嵌套一般不超过 15 层;超过就该考虑迭代+显式栈,或拒绝处理。
- 测试时用
@pytest.mark.parametrize覆盖[1, 10, 999, 1000, 1001]这类临界值,并加@pytest.mark.timeout(0.1)防止单个用例拖垮测试套件。 - 运行中可用
len(inspect.stack())断言实际调用深度,确认没因逻辑缺陷意外加深。
解耦调用路径,让错误传播可验证
递归层层嵌套时,你得知道错在哪一层、上层怎么响应。
- 用
patch.object替换某一层的递归调用,比如让第 3 层固定抛ValueError,然后验证第 2 层是否捕获并返回默认值或重试,而不是让整个调用静默失败。 - mock 必须打在被测模块中实际 import 的函数名上(例如
my_module.traverse),不是定义位置,否则 patch 失效。 - 每次 mock 后检查
mock_func.assert_called_once(),确保你跳过的确实是那一次递归,验证逻辑真实生效。
测试覆盖三类输入:合法、边界、非法
健壮性测试不是多跑几个数,而是系统性制造“不友好但真实”的场景。
- 合法输入:覆盖所有分支路径,比如树遍历中叶子节点、单子节点、多子节点各测一遍。
- 边界输入:
n=0、空数组、深度为 1 的树、最大允许嵌套层级。 - 非法输入:
None、NaN、超长字符串、类型错乱(数字传成字符串)、嵌套过深的 JSON;再微小扰动一下——把{"a": 1}改成{"a": "1"},看是否触发类型校验。
不复杂但容易忽略。











