Today I realized that it matters for unittest.mock.patch
how I import a function. Depending on the used way a mock.patch
call worked or was ignored. In Python we typically import a function with:
import os
orfrom ... import ...
statement like from os import system
A mock.patch
works like a charm if I use import os
, but it
was ignored if I patch a from os import system
.
Example 1: Using import
import os
from unittest import mock
def echo():
os.system('echo "Hello"')
with mock.patch('os.system') as mocked:
print(mocked)
mocked.side_effect = Exception('Patch works!')
echo()
Output of example 1
<MagicMock name='system' id='140037358656760'>
Traceback (most recent call last):
File "/.../config/scratches/scratch_7.py", line 12, in <module>
echo()
File "/.../config/scratches/scratch_7.py", line 6, in echo
os.system('echo "Hello"')
File "/.../python3.5/unittest/mock.py", line 917, in __call__
return _mock_self._mock_call(*args, **kwargs)
File "/.../python3.5/unittest/mock.py", line 973, in _mock_call
raise effect
Exception: Patch works!
Example 2: Using a full function import and from-import
When I fully import os.system
the mock.patch
ignores the mocked.side_effect
.
from os import system
from unittest import mock
def echo():
system('echo "Hello"')
with mock.patch('os.system') as mocked:
print(mocked)
mocked.side_effect = Exception('Patching does not work!')
echo()
print('Patch was ignored!')
Output of example 2
<MagicMock name='system' id='139851175427376'>
Hello
Patch was ignored!
In both cases I don't receive an error and mock
could find os.system
as a valid path. However, in the second case the function is not properly patched.
mock.patch
does not patch the function in the second example?When you do from os import system
, you get a variable named system
pointing to os.system
function. Later, you assign, via patching, a different function to os.system
, but system
keeps to point to the old function. This is the same reason why the following works:
tmp = a
a = b
b = tmp
It doesn't happen in the first example, because you reference os.system
before it is mocked. To fix your second example, I'd go with the following:
from os import system
from unittest import mock
def echo():
system('echo "Hello"')
with mock.patch('__main__.system') as mocked:
print(mocked)
mocked.side_effect = Exception('Patching does not work!')
echo()
print('Patch was ignored!')
This way you make sure you patch the right reference. This is a rather common pattern. If echo
function were in a file named echo.py
, the patch call would look like with mock.patch('echo.system')
.