递归函数测试时遇 RecursionError 应临时调高栈限制,如用 sys.setrecursionlimit(3000) 并及时恢复;边界用例需覆盖 base case、单层递归、临界输入及异常输入;mock 须正确指定 target 路径;参数化测试应使用可控结构数据而非随机数据。

递归函数测试时 pytest 报 RecursionError 怎么办
pytest 默认的调用栈深度和普通 Python 解释器一致,而递归算法(比如深度优先遍历、阶乘、快速排序的分区递归)在输入稍大时就容易触发 RecursionError: maximum recursion depth exceeded。这不是测试写错了,而是 pytest 运行环境没做栈深度适配。
解决方法不是改算法,而是临时调高限制——但必须仅限测试作用域,避免污染全局或影响其他测试:
- 在具体测试函数开头用
sys.setrecursionlimit(3000),并在结尾用sys.setrecursionlimit(1000)恢复(Python 默认值通常为 1000) - 更稳妥的做法是用
pytest.fixture+autouse=True在 setup/teardown 中控制,但仅对含递归的测试类启用 - 避免在
conftest.py全局设置,否则可能掩盖真实栈溢出问题,或导致其他测试意外失败
如何为递归函数设计边界覆盖用例
递归函数的正确性高度依赖 base case 和 recursive case 的衔接,只测几个“看起来能跑通”的输入远远不够。重点不是穷举,而是卡住三个关键点:base case 触发、单层递归、以及刚好越过栈安全阈值的临界输入。
例如测试一个计算斐波那契的 fib(n):
立即学习“Python免费学习笔记(深入)”;
-
fib(0)和fib(1)必须返回 0/1,验证 base case 正确性 -
fib(2)和fib(3)强制走一次和两次递归调用,确认递推逻辑不崩 -
fib(50)(非暴力,用记忆化或迭代验证结果)检查大输入下是否仍收敛,而非无限递归或数值溢出 - 传入负数如
fib(-1),看是否抛出预期异常(比如ValueError),而不是静默返回错误值或崩溃
mock 递归调用链中的某一层是否可行
直接 mock fib 自身来打断递归链,在 pytest 中是可行的,但要小心 patch 的 target 路径和作用域。常见错误是 mock 了错误位置,导致被测函数实际仍执行原递归逻辑。
假设 fib 定义在 utils.py 中,你在 test_utils.py 里测试:
- 正确 patch 写法是
@patch('utils.fib'),不是@patch('test_utils.fib')或@patch('__main__.fib') - 若想模拟“第二层递归失败”,可让 mock 在第一次调用返回正常值,第二次调用抛异常:
mock_fib.side_effect = [1, RuntimeError("simulated deep error")] - 注意:mock 后函数不再真正递归,所以无法验证栈行为或性能,仅适合逻辑分支隔离测试
pytest 参数化测试递归深度与输入组合
用 @pytest.mark.parametrize 能高效覆盖多组输入,但对递归函数要注意参数间隐含的深度关系。比如测试树遍历函数 count_nodes(node),不能只给不同 node,还要确保这些 node 构造出已知深度的子树。
推荐做法:
- 用辅助函数生成可控结构的数据,例如
make_binary_tree(height=3)返回一棵确定深度的树 - 参数化列表里显式带上
expected_depth和expected_count,让断言可读且可追溯 - 避免用随机生成数据做 parametrize,否则失败时无法复现,也不利于判断是算法缺陷还是测试数据异常
递归测试最难的从来不是写断言,而是构造可验证、可复现、带深度语义的输入;一旦输入不可控,再多的 pytest 高级特性也救不回模糊的失败原因。


















