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")
I use python3:
pie@pie:~$ python3 --version
Python 3.6.9
For above code, I expect to get next because I have mocked hello
:
start
wow
end
But actually, I got next:
pie@pie:~$ python3 b.py
start
start
wow
end
hello
1
end
I have totally confused by the mock behavior, what happened?
Update the answer in case anyone interested, the root cause is patch target
and patch
in same file. There won't have any issue if not in the same file.
From the sourcecode of patch function
in unittest/mock.py
, we could see in fact it use __import__
to import the target:
patch
-> getter, attribute = _get_target(target)
-> getter = lambda: _importer(target)
-> thing = __import__(import_path)
So above code in question behave as next:
b.py
start, it first print start
fun()
, at this point, the patch
will use __import__
to import b.hello
. So b.py
now act as a python module which be executed again. As a python module, it print start
.fun
, as module already imported, so this won't start another import. As a module run, hello()
really means b.hello()
, which already patched, it should returns wow
. So, wow
now be printed.end
.b.py
as a top level script continue run hello()
, but now the patch
indeed patched b.hello
, not hello
, so it still print hello
& 1
.end
.