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?patch() unittest. mock provides a powerful mechanism for mocking objects, called patch() , which looks up an object in a given module and replaces that object with a Mock . Usually, you use patch() as a decorator or a context manager to provide a scope in which you will mock the target object.
Rather than hacking on top of Python's import machinery, you can simply add the mocked module into sys. path , and have Python prefer it over the original module. Now, when the test suite is run, the mocked-lib subdirectory is prepended into sys. path and import A uses B from mocked-lib .
So what is the difference between them? MagicMock is a subclass of Mock . It contains all magic methods pre-created and ready to use (e.g. __str__ , __len__ , etc.). Therefore, you should use MagicMock when you need magic methods, and Mock if you don't need them.
Mock is a type, and patch is a context. So you are going to pass or receive Mock instances as parameters, and apply patch contexts to blocks of code. (Lowercase 'mock' is just the name of the package.) Tests and test classes are often decorated with calls to patch.
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')
.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With