修改一个项目的 Unit Test 代码时,遇到一个关于 mock
的问题点,花了我很久时间才,找到解决方案。特此记录一下,提醒自己,提示他人。
我给一个开源软件贡献 PR 时,遇到一个大的问题是我总是要给我的代码写单元测试,会出现一些奇怪的问题(我以为的,实际最后往往证明是我蠢 :( ,本例就是)
经过简化,最小可复现问题代码集如下:
文件 dependency.py
def some_funny_func():
return 'funny'
文件 module.py
from dependency import some_funny_func
def call_func():
return some_funny_func()
文件 tester.py
from unittest.mock import patch
from module import call_func
def test_call_func():
def mocked_funny_func():
return "not funny at all"
with patch("dependency.some_funny_func", mocked_funny_func):
return_value = call_func()
assert return_value == "not funny at all"
一切看似合情合理(有些高手,可能已经发现问题了,但我当时没有看出来问题),但是就是通过不了测试。
这里的错误是,没有深入理解 patch
的工作原理,patch
通过修改 module 属性的方式工作。这里 from module import call_func
执行的时候已经经 dependency.some_funny_func
导入了 module
, 换言之:module.some_funny_func
已经指向了 dependency.some_funny_func
, 此时通过 patch("dependency.some_funny_func", mocked_funny_func)
只是修改了 dependency.some_funny_func
至新的 mocked_funny_func
. 但不能修改 module.some_funny_func
, 因为这个是修改前赋值的,它现在依旧指向原来的函数。
将上述改写成 python 代码,原理大概如下:
# 模拟 dependency.some_funny_func
dependency = {}
dependency["some_funny_func"] = "some_value"
# 模拟 module.some_funny_func
module = {}
module["some_funny_func"] = dependency["some_funny_func"]
# 模拟 mock
dependency["some_funny_func"] = "some_other_value"
# 查看结果
print(dependency["some_funny_func"])
print(module["some_funny_func"])