Swaroop
4 min readApr 25, 2020

Mocking Functions Part II | Writing Better Tests in Python with pytest-mock

In the previous article on mocking, we have seen why mocking is useful and how to install pytest-mock. We have also seen how we can mock a slow function to make our test suite faster.

Mocking with pytest

In this article, we will learn a few more ways of mocking functions. To recap, we had a module named application1.py with two functions.

# application1.py

from time import sleep

def is_windows():
# This sleep could be some complex operation instead
sleep(5)
return True


def get_operating_system():
return 'Windows' if is_windows() else 'Linux'

The get_operating_system function depends on is_windows function, which was slow (simulated with sleep). To be able to test get_operating_system, we decided to mock the is_windows function.

The test function was as follows:

# test_application1.py

from application1 import get_operating_system

# mocker is a fixture provided by "pytest-mock"
def test_get_operating_system(mocker):
mocker.patch('application1.is_windows', return_value=True)
assert get_operating_system() == 'Windows'

With the patch, the is_windows function will not take 5 seconds anymore and will return a value immediately.

This can be visualized as shown below. The default flow where get_operating_system calls is_windows would be as follows:

mock visualization

When we do the patch, we create a new mocked function that gets called, bypassing the original function.

mock visualization

That is how mocks work. They replace the original objects with a dummy.

Now, Let’s switch things up a bit. Say, the is_windows function, instead of being part of application, is imported from another module called windows_utils.

# windows_utils.py

from time import sleep

def is_windows():
# This sleep could be some complex operation instead
sleep(5)
return True

We import this function into our application module.

# application2.py

from windows_utils import is_windows

def get_operating_system():
return 'Windows' if is_windows() else 'Linux'

And it is the same story again here. We need to mock the slow function. Now, the is_windows function is coming from windows_utils module. So, you might be tempted to do as follows:

mocker.patch('windows_utils.is_windows', return_value=True)

But that will not work the way you might expect. When it comes to mocking, we always mock where the function is used. Even though the is_windows is from the windows_utils module, since it is being used in application2.py, we have to mock application2.is_windows instead. This is because of how imports work in python. In Python, whenever you import something into a module, it becomes part of the module. So, you refer to it always as <module>.<imported_thing>.

So, the test function would remain same as before.

# test_application2.py

from application2 import get_operating_system

def test_get_operation_system(mocker):
mocker.patch('application2.is_windows', return_value=True)
assert get_operating_system() == 'Windows'

As discussed above, your mocking target should reflect how something is imported. In application2.py we have imported is_windows with from windows_utils import is_windows. Hence, our mock target was application2.is_windows.

Instead, if we just do import windows_utils, our mocking target would also need to change. Let us see how this works with an example.

# application3.py

import windows_utils

def get_operating_system():
return 'Windows' if windows_utils.is_windows() else 'Linux'

Now the mock target for ‘is_windows’ is no longer application3.is_windows as there is no direct reference to 'is_windows' in 'application3'. Instead we use application3.windows_utils.is_windows. That is the fully qualified reference. The new test is as follows:

# test_application3.py

from application3 import get_operating_system

def test_get_operation_system(mocker):
mocker.patch('application3.windows_utils.is_windows', return_value=True)
assert get_operating_system() == 'Windows'

Instead of using application3.windows_utils.is_windows, we can simplify it to windows_utils.is_windows as well. But referring to everything as application3.<something> keeps it clean and easy to understand.

That is all for this article. We will see how we can mock objects in the next article. Stay tuned.

The full list of articles in this series:

Mocking Functions Part I
Mocking Functions Part II🢠 Current Article

If you like this article, you can clap it to encourage me to put out the next article soon. If you think someone you know can benefit from this article, do share it with them.

If you want to thank me, you can say hi on twitter @durgaswaroop. And, if you want to support me here is my paypal link: paypal.me/durgaswaroop

Attribution: Python Logo — https://www.python.org/community/logos/