Python unittest.mock.patch对函数参数默认值不生效问题求解
问题成因
Python 中函数的默认参数只会在函数定义阶段求值一次,求值结果会被保存在函数的__defaults__属性中,后续调用函数时如果不传该参数,会直接复用第一次的求值结果,不会重新计算。
你定义print_a时,默认参数file=sys.stdout直接取了函数定义时真实系统标准输出的引用,后续你用@patch修改的只是sys.stdout这个变量的指向,并没有改动print_a里已经保存的旧stdout引用,因此print_a的输出还是会写到原始的标准输出里,不会被mock捕获。
而print_b没有提前缓存sys.stdout,每次调用时都会实时读取当前sys.stdout的指向,此时已经被patch替换成了mock对象,因此输出可以被正常捕获。
另外你写的@patch装饰器存在拼写错误:new_callablle多写了一个字母l,正确写法为new_callable,这个错误也会导致mock逻辑无法按预期生效。
解决方法
- 方案1:修改
print_a的参数定义,避免在函数定义阶段就求值sys.stdout:
def print_a(file=None): """ 调用时再获取当前的sys.stdout,可被正常mock """ if file is None: file = sys.stdout print("a", file=file)
- 方案2:如果不能修改原函数的实现,测试时主动传入当前的
sys.stdout即可:
# 测试调用时主动传参 test_print(lambda: print_a(file=sys.stdout))
- 方案3:也可以在测试时直接patch
print_a的默认参数,不过这种方式侵入性更强,不推荐优先使用。
内容的提问来源于stack exchange,提问作者Dave Fol
相关产品推荐
相关产品推荐

