为什么模拟补丁不能按预期工作?
Why mock patch not works as expected?
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
我完全被模拟行为弄糊涂了,发生了什么事?
如果有人感兴趣,请更新答案,根本原因是同一文件中的 patch target
和 patch
。不在同一个文件里就不会有问题。
从unittest/mock.py
中的patch function
的源代码中,我们可以看到实际上它使用__import__
来导入目标:
patch
-> getter, attribute = _get_target(target)
-> getter = lambda: _importer(target)
-> thing = __import__(import_path)
所以上面有问题的代码的行为如下:
- 作为顶级脚本
b.py
启动,它首先打印 start
- 作为顶级脚本,它调用
fun()
,此时,patch
将使用__import__
导入b.hello
。所以 b.py
现在作为一个 python 模块再次执行。作为 python 模块,它打印 start
.
- 作为 python 模块,它调用
fun
,因为模块已经导入,所以这不会启动另一个导入。作为一个模块 运行,hello()
实际上意味着 b.hello()
,已经打补丁了,它应该 returns wow
。所以,wow
现在被打印出来了。
- 作为python模块,它最终打印出
end
.
- 现在 python 模块执行完成,
b.py
作为顶级脚本继续 运行 hello()
,但现在 patch
确实已修补 b.hello
,而不是 hello
,所以它仍然打印 hello
& 1
.
- 最后,作为 python 顶级脚本它打印
end
。
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
我完全被模拟行为弄糊涂了,发生了什么事?
如果有人感兴趣,请更新答案,根本原因是同一文件中的 patch target
和 patch
。不在同一个文件里就不会有问题。
从unittest/mock.py
中的patch function
的源代码中,我们可以看到实际上它使用__import__
来导入目标:
patch
-> getter, attribute = _get_target(target)
-> getter = lambda: _importer(target)
-> thing = __import__(import_path)
所以上面有问题的代码的行为如下:
- 作为顶级脚本
b.py
启动,它首先打印start
- 作为顶级脚本,它调用
fun()
,此时,patch
将使用__import__
导入b.hello
。所以b.py
现在作为一个 python 模块再次执行。作为 python 模块,它打印start
. - 作为 python 模块,它调用
fun
,因为模块已经导入,所以这不会启动另一个导入。作为一个模块 运行,hello()
实际上意味着b.hello()
,已经打补丁了,它应该 returnswow
。所以,wow
现在被打印出来了。 - 作为python模块,它最终打印出
end
. - 现在 python 模块执行完成,
b.py
作为顶级脚本继续 运行hello()
,但现在patch
确实已修补b.hello
,而不是hello
,所以它仍然打印hello
&1
. - 最后,作为 python 顶级脚本它打印
end
。