How to Mock Function in Python

Migel Hewage Nimesha Feb 15, 2024
  1. Set Up and Install Pytest Mock in Python
  2. Mock a Function in Python
  3. Conclusion
How to Mock Function in Python

The unittest.mock or Mock function is a library for testing in Python that allows you to replace components of your system under test with mock objects and make assertions about how the parts have been used.

The unittest.mock gives a core Mock class, removing the necessity to create a host of stubs all over your test suite.

After executing a process, you can assert which methods or attributes were used and the arguments with which they were called. You can also specify the return values and set the needed attributes in the usual way.

In addition, Mock provides a patch() decorator that can handle the patching module and the class level attributes within the test scope, along with a helper sentinel, for creating unique objects.

Mock was created for use with unittest and is based on the action-to-assertion pattern instead of record-to-replay, which is used in most mocking frameworks. There is a backport of unittest.mock for previous versions of Python.

Set Up and Install Pytest Mock in Python

In contrast to unittest, pytest is not a built-in Python package and needs installation. This can be installed by entering the below command in the terminal.

pip install pytest

As a note, the best practices for unittest also apply to pytest, such that:

  1. A tests/ directory is required to contain all unit tests.
  2. File names must always begin with tests_.
  3. Function names must always begin with the test.

The naming standards must be observed so the checker can find the unit tests performed.

Installing pytest-mock is required before you can use it. The following is how to install it using pip:

pip install pytest-mock

This is a plugin for Pytest. Therefore, if you haven’t already done so, it will install Pytest.

Mock a Function in Python

To start, create a simple function get_os() to tell us whether we are using Windows or Linux as the operating system.

Create a file named app.py as follows:

from time import sleep


def isWindows():
    # This sleep can be some complex operation
    sleep(5)
    return True


def get_os():
    return "Windows is the current OS" if isWindows() else "Linux is the current OS"

This function uses the isWindows function to determine whether the current operating system is Windows or not. Assuming that this isWindows function is quite complex and takes several seconds to run, here we can simulate this slow-going function by entering the program to sleep for 5 seconds every time it is called.

The following would be a pytest for the get_os() function:

Create a file named test_app.py to pytest:

from app import get_os


def test_get_os():
    assert get_os() == "Windows is the current OS"

Since get_os() calls a slower function isWindows, the test is proceeding to be slow. This can be seen in the below output of executing the pytest.

It took 5.02 seconds and can vary depending on the current instance.

mock function python - output 1

Unit testing needs to be quick, and we should be able to perform hundreds of tests in a matter of seconds. The test suite is slowed down by a single test that takes 5 seconds, so applying mocking makes our lives easier.

If we patch the slow-going function, we can verify the get_os() function’s behavior without staying for 5 seconds.

Let us mock this function with pytest-mock.

Pytest-mock provides a fixture called a mocker and a fine interface on top of Python’s integrated mocking constructs. You can employ a mocker by invoking the mock and patch functions from it and sending them as arguments to your test function.

In a case where you want the isWindows function to return True without waiting for those precious 5 seconds, we can patch it as follows:

mocker.patch("app.isWindows", return_value=True)

Here, you have to refer to isWindows as app.isWindows, given that it is the function in the app module. If we only patch isWindows, it will try to patch a function called isWindows in the test_app file, which does not exist.

The format is always <module_name>.<function_name>, and knowing how to mock correctly is essential.

The following describes the modified test function after the patch:

from app import get_os

# using'mocker' fixture provided by pytest-mock


def test_get_os(mocker):
    # mocking the slow-going function and returning True always
    mocker.patch("app.isWindows", return_value=True)
    assert get_os() == "Windows is the current OS"

Now, this test will finish much more quickly when you run it.

mock function python - output 2

As you can see, the test took only 0.02 seconds, so we have successfully patched the slow-going function and accelerated the test suite.

Another advantage of mocking is that you can make the mock function return anything and can even make it raise errors to test how your code processes in those scenarios.

Here, if you want to test the case where isWindows returns False, write the following test:

from app import get_os


def test_os_isLinux(mocker):
    mocker.patch(
        "app.isWindows", return_value=False
    )  # set the return value to be False
    assert get_os() == "Linux is the current OS"

The mocks and patches that come with the mocker are all function-scoped, meaning they can only be used with that particular function. As a result, there won’t be any conflicts between patches for the same function in different tests.

Conclusion

Mocking is the practice of replacing the application component you are testing with a mock, which is a dummy implementation of that component. Through mocking, we can get benefits such as increased speed, where tests that run faster are highly beneficial, avoiding undesired side effects during testing, and so on.

Migel Hewage Nimesha avatar Migel Hewage Nimesha avatar

Nimesha is a Full-stack Software Engineer for more than five years, he loves technology, as technology has the power to solve our many problems within just a minute. He have been contributing to various projects over the last 5+ years and working with almost all the so-called 03 tiers(DB, M-Tier, and Client). Recently, he has started working with DevOps technologies such as Azure administration, Kubernetes, Terraform automation, and Bash scripting as well.

Related Article - Python Mock