为什么模拟补丁无法按预期工作?

时间:2020-03-10 07:41:19

标签: python python-3.x mocking stub

b.py:

from unittest.mock import patch

def hello():
    print("hello")
    return 1

@patch("b.hello", return_value="wow")
def fun(mock_hello):
    print(hello())

print("start")
fun()
print("end")

我使用python3:

pie@pie:~$ python3 --version
Python 3.6.9

对于上述代码,我希望得到下一个,因为我嘲笑了hello

start
wow
end

但实际上,我是下一个:

pie@pie:~$ python3 b.py
start
start
wow
end
hello
1
end

我对模拟行为完全感到困惑,发生了什么事?

1 个答案:

答案 0 :(得分:0)

如果有兴趣的人更新答案,则根本原因是patch targetpatch在同一文件中。如果不在同一文件中,则不会有任何问题。

patch functionunittest/mock.py的源代码中,我们实际上可以看到它使用__import__来导入目标:

patch-> getter, attribute = _get_target(target)-> getter = lambda: _importer(target)-> thing = __import__(import_path)

因此上述代码如下:

  1. 顶级脚本b.py启动时,它首先打印start
  2. 作为顶级脚本,它调用fun(),此时patch将使用__import__来导入b.hello。因此b.py现在充当了一个再次执行的python模块。作为python模块,它输出start
  3. 作为python模块,它调用fun,因为已经导入了模块,因此不会启动其他导入。在运行模块时,hello()的实际含义是已经修补的b.hello(),它应该返回wow。因此,wow现在可以打印了。
  4. 作为python模块,它最终打印end
  5. 现在python模块执行完毕,b.py作为顶级脚本继续运行hello(),但是现在patch确实已修补b.hello,而不是hello,因此它仍会打印hello1
  6. 最后,它作为python顶级脚本打印end