Understanding the Python Mock Object Library

Understanding the Python Mock Object Library

by Alex Ronquillo Jun 26, 2024 intermediate testing

Watch Now This tutorial has a related video course created by the Real Python team. Watch it together with the written tutorial to deepen your understanding: Improve Your Tests With the Python Mock Object Library

Python’s unittest.mock library is a tool that helps you create mock objects to simulate complex logic and unpredictable dependencies. This helps you write valuable tests that verify your application logic is correct, reliable, and efficient.

By the end of this tutorial, you’ll be able to:

  • Create Python mock objects using Mock
  • Assert that you’re using objects as you intended
  • Inspect usage data stored on your Python mocks
  • Configure certain aspects of your Python mock objects
  • Substitute your mocks for real objects using patch()
  • Avoid common problems inherent in Python mocking

You’ll begin by learning about what mocking is and how you can use it to improve your tests.

Take the Quiz: Test your knowledge with our interactive “Understanding the Python Mock Object Library” quiz. You’ll receive a score upon completion to help you track your learning progress:


Interactive Quiz

Understanding the Python Mock Object Library

In this quiz, you'll test your understanding of Python's unittest.mock library. With this knowledge, you'll be able to write robust tests, create mock objects, and ensure your code is reliable and efficient.

What Is Mocking?

A mock object substitutes and imitates a real object within a testing environment. Using mock objects is a versatile and powerful way to improve the quality of your tests. This is because by using Python mock objects, you can control your code’s behavior during testing.

For example, if your code makes HTTP requests to external services, then your tests execute predictably only so far as the services are behaving as you expected. Sometimes, a temporary change in the behavior of these external services can cause intermittent failures within your test suite.

Because of this, it would be better for you to test your code in a controlled environment. Replacing the actual request with a mock object would allow you to simulate external service outages and successful responses in a predictable way.

Sometimes, it’s difficult to test certain areas of your codebase. Such areas include except blocks and if statements that are hard to satisfy. Using Python mock objects can help you control the execution path of your code to reach these areas and improve your code coverage.

Another reason to use mock objects is to better understand how you’re using their real counterparts in your code. A Python mock object contains data about its usage that you can inspect, such as:

  • If you called a method
  • How you called the method
  • How often you called the method

Understanding what a mock object does is the first step to learning how to use one. Next, you’ll explore the Python mock object library to see how to use Python mock objects.

The Python Mock Library

Python’s built-in mock object library is unittest.mock. It provides an easy way to introduce mocks into your tests.

unittest.mock provides a class called Mock, which you’ll use to imitate real objects in your codebase. Mock, along with its subclasses, offers incredible flexibility and insightful data that will meet most of your Python mocking needs.

The library also provides a function called patch(), which replaces the real objects in your code with Mock instances. You can use patch() as either a decorator or a context manager, giving you control over the scope in which the object will be mocked. Once the designated scope exits, patch() will clean up your code by replacing the mocked objects with their original counterparts.

Finally, unittest.mock provides solutions for some of the issues inherent in mocking objects, which you’ll explore later in this tutorial.

Now that you have a better understanding of what mocking is and the library you’ll be using, it’s time to dive in and explore the features and functionalities unittest.mock has to offer.

The Mock Object

unittest.mock offers a base class for mocking objects called Mock. The use cases for Mock are practically limitless because Mock is so flexible.

Begin by instantiating a new Mock instance:

Python
>>> from unittest.mock import Mock
>>> mock = Mock()
>>> mock
<Mock id='4561344720'>

Now, you’re able to substitute an object in your code with your new Mock. You can do this by passing it as an argument to a function or by redefining another object:

Python
# Pass mock as an argument to do_something()
do_something(mock)

# Patch the json library
json = mock

When you substitute an object in your code, the Mock must look like the real object it’s replacing. Otherwise, your code will not be able to use the Mock in place of the original object.

For example, if you’re mocking the json library and your program calls dumps(), then your Python mock object must also contain dumps(). Next, you’ll see how Mock deals with this challenge.

Understanding Lazy Attributes and Methods

A Mock must simulate any object that it replaces. To achieve such flexibility, it creates its attributes when you access them :

Python
>>> mock.some_attribute
<Mock name='mock.some_attribute' id='4394778696'>
>>> mock.do_something()
<Mock name='mock.do_something()' id='4394778920'>

Since Mock can create arbitrary attributes on the fly, it’s able to replace any object.

Using an example from earlier, if you’re mocking the json library and you call dumps(), the Python mock object will create the method so that its interface can match the library’s interface:

Python
>>> json = Mock()
>>> json.dumps()
<Mock name='mock.dumps()' id='4392249776'>

Notice two key characteristics of this mocked version of dumps():

  1. Unlike the real dumps(), this mocked method requires no arguments. In fact, it will accept any arguments that you pass to it.

  2. The return value of .dumps() is also a Mock.

The capability of Mock to recursively define other mocks allows for you to use mocks in complex situations:

Python
>>> json = Mock()
>>> json.loads('{"k": "v"}').get("k")
<Mock name='mock.loads().get()' id='4379599424'>

Because the return value of each mocked method is also a Mock, you can use your mocks in a multitude of ways.

Mocks are flexible, but they’re also informative. Next, you’ll learn how you can use mocks to understand your code better.

Leveraging Assertions and Inspection

Mock instances store data on how you used them. For example, they allow you to see if you called a method, how you called the method, and so on. There are two main ways to use this information:

  1. Assertions allow you to assert that you used an object as expected.
  2. Inspection allows you to to view special attributes to understand how your application used an object.

First, you’ll look at how you can use assertions. Set up a new Mock object, again to mock the json library, then call .loads():

Python
>>> from unittest.mock import Mock
>>> json = Mock()

>>> json.loads('{"key": "value"}')
<Mock name='mock.loads()' id='4550144184'>

You’ve created a new Mock, mock.loads(), and called it. You can now make assertions to test your expectations that you called .loads() once:

Python
>>> json.loads.assert_called()
>>> json.loads.assert_called_once()
>>> json.loads.assert_called_with('{"key": "value"}')
>>> json.loads.assert_called_once_with('{"key": "value"}')

All assertions pass without raising an AssertionError. Try to call .loads() again and also repeat the call to .assert_called_once():

Python
>>> json.loads('{"key": "value"}')
<Mock name='mock.loads()' id='4550144184'>

>>> json.loads.assert_called_once()
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "/path/to/python/unittest/mock.py", line 918, in assert_called_once
    raise AssertionError(msg)
AssertionError: Expected 'loads' to have been called once. Called 2 times.
Calls: [call('{"key": "value"}'), call('{"key": "value"}')].

Your assertion .assert_called_once() fails and raises an AssertionError because you called .loads() two times.

A mock object comes with a variety of assertion methods, such as:

  • .assert_called(): Ensures that you called the mocked method.
  • .assert_called_once(): Checks that you called the method exactly one time.
  • .assert_not_called(): Ensures that you never called the mocked method.

It also has similar methods that let you inspect the arguments passed to the mocked method:

  • .assert_called_with(*args, **kwargs)
  • .assert_called_once_with(*args, **kwargs)

To pass these assertions, you must call the mocked method with the same arguments that you pass to the actual method.

This approach can break when you specify the arguments differently in both calls, even if you provide the same arguments:

Python
>>> json = Mock()
>>> json.loads(s='{"key": "value"}')
<Mock name='mock.loads()' id='4421942864'>

>>> json.loads.assert_called_with('{"key": "value"}')
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "/path/to/python/unittest/mock.py", line 939, in assert_called_with
    raise AssertionError(_error_message()) from cause
AssertionError: expected call not found.
Expected: loads('{"key": "value"}')
Actual: loads(s='{"key": "value"}')

>>> json.loads.assert_called_with(s='{"key": "value"}')

You can see that the first call to .assert_called_with() raised an AssertionError because it expected you to call .loads() with a positional argument, but you actually called it with a keyword argument. When you call .assert_called_with() the second time, using the keyword argument, the assertion passes.

Equipped with these built-in methods on a Mock, you can come a long way in asserting that your program used an object as you expected. But there’s even more you can do.

You can introspect your Mock by accessing special attributes. This gives you an even better idea of how your application used an object.

Start by creating a fresh mock and calling .loads() on it again:

Python
>>> from unittest.mock import Mock

>>> json = Mock()
>>> json.loads('{"key": "value"}')
<Mock name='mock.loads()' id='4391026640'>

Instead of using assertion methods like before, you can inspect the value of some special attributes:

Python
>>> # Number of times you called loads():
>>> json.loads.call_count
1

>>> # The last loads() call:
>>> json.loads.call_args
call('{"key": "value"}')

>>> # List of loads() calls:
>>> json.loads.call_args_list
[call('{"key": "value"}')]

>>> # List of calls to json's methods (recursively):
>>> json.method_calls
[call.loads('{"key": "value"}')]

You can write tests using these attributes to make sure that your objects behave as you intended.

Now, you can create mocks and inspect their usage data. Next, you’ll see how to customize mocked methods so that they become more useful in your testing environment.

Managing a Mock’s Return Value

One reason to use mocks is to control your code’s behavior during tests. One way to do this is to specify a function’s return value. Let’s use an example to see how this works.

First, create a file called holidays.py. Add is_weekday(), a function that uses Python’s datetime library to determine whether or not today is a weekday. Finally, write a test that asserts that the function works as expected:

Python holidays.py
from datetime import datetime

def is_weekday():
    today = datetime.today()
    # Python's datetime library treats Monday as 0 and Sunday as 6
    return (0 <= today.weekday() < 5)

# Test if today is a weekday
assert is_weekday()

Since you’re testing if today is a weekday, the result depends on the day you run your test:

Shell
$ python holidays.py

If this command produces no output, the assertion was successful. Unfortunately, if you run the command on a weekend, you’ll get an AssertionError:

Shell
$ python holidays.py
Traceback (most recent call last):
  File "/path/to/holidays.py", line 9, in <module>
    assert is_weekday()
AssertionError

When writing tests, it is important to ensure that the results are predictable. You can use Mock to eliminate uncertainty from your code during testing. In this case, you can mock datetime and set the .return_value for .today() to a day that you choose:

Python holidays.py
from datetime import datetime
from unittest.mock import Mock

# Save a couple of test days
wednesday = datetime(year=2025, month=1, day=1)
sunday = datetime(year=2025, month=1, day=5)

# Mock datetime to control today's date
datetime = Mock()

def is_weekday():
    today = datetime.today()
    # Python's datetime library treats Monday as 0 and Sunday as 6
    return (0 <= today.weekday() < 5)

# Mock .today() to return Wednesday
datetime.today.return_value = wednesday
# Test Wednesday is a weekday
assert is_weekday()

# Mock .today() to return Sunday
datetime.today.return_value = sunday
# Test Sunday is not a weekday
assert not is_weekday()

In the example, .today() is a mocked method. You’ve removed the inconsistency by assigning a specific day to the mock’s .return_value. That way, when you call .today(), it returns the datetime that you specified.

In the first test, you ensure wednesday is a weekday. In the second test, you verify that sunday is not a weekday. Now, it doesn’t matter what day you run your tests because you’ve mocked datetime and have control over the object’s behavior.

Now that you know how to mock a function’s return value, you can write predictable tests for your code, even when it depends on unpredictable outside circumstances.

When you write tests for your program, you generally place them in a separate file. Following good practice, you’d set up a test_holidays.py to separate the holiday calendar logic from your testing logic, and import the functions from holidays.py:

Python test_holidays.py
from datetime import datetime
from unittest.mock import Mock

from holidays import is_weekday

# Save a couple of test days
wednesday = datetime(year=2025, month=1, day=1)
sunday = datetime(year=2025, month=1, day=5)

# Mock datetime to control today's date
datetime = Mock()

# Mock .today() to return Wednesday
datetime.today.return_value = wednesday
# Test Wednesday is a weekday
assert is_weekday()

# Mock .today() to return Sunday
datetime.today.return_value = sunday
# Test Sunday is not a weekday
assert not is_weekday()

However, if you do this, then the crude approach to mocking that you’ve seen so far will break your test code:

Shell
$ python test_holidays.py
Traceback (most recent call last):
  File "/path/to/test_holidays.py", line 21, in <module>
    assert not is_weekday()
AssertionError

The reason for this error comes from how you mocked the datetime module in your test file. When you import a module in another module, the imported names are bound to that module’s namespace. Mocking datetime in your test file doesn’t affect datetime in is_weekday() because holidays.py has already imported the real datetime module.

To correctly mock datetime in holidays.py, you should patch datetime in the namespace where it’s used. The unittest.mock library’s patch() function is useful for this purpose. You’ll learn how to use patch() later on. For now, you’ll continue mocking right inside holidays.py to avoid this issue.

When building your tests, you’ll likely come across cases where mocking a function’s return value will not be enough. This is because functions are often more complicated than a simple one-way flow of logic.

Sometimes, you’ll want to make functions return different values when you call them more than once or even raise exceptions. You can do this using .side_effect.

Managing a Mock’s Side Effects

You can control your code’s behavior by specifying a mocked function’s side effects. A .side_effect defines what happens when you call the mocked function.

To test how this works, add a new function to holidays.py:

Python holidays.py
# ...

import requests

def get_holidays():
    r = requests.get("http://localhost/api/holidays")
    if r.status_code == 200:
        return r.json()
    return None

# ...

In this example, get_holidays() makes a request to the localhost server for a set of holidays. If the server responds successfully, get_holidays() will return a dictionary. Otherwise, the method will return None.

You can test how get_holidays() will respond to a connection timeout by setting requests.get.side_effect.

For this example, you’ll only see the relevant code from holidays.py. You’ll build a test case using Python’s unittest library:

Python holidays.py
import unittest
from requests.exceptions import Timeout
from unittest.mock import Mock

# Mock requests to control its behavior
requests = Mock()

def get_holidays():
    r = requests.get("http://localhost/api/holidays")
    if r.status_code == 200:
        return r.json()
    return None

class TestHolidays(unittest.TestCase):
    def test_get_holidays_timeout(self):
        # Test a connection timeout
        requests.get.side_effect = Timeout
        with self.assertRaises(Timeout):
            get_holidays()

if __name__ == "__main__":
    unittest.main()

You use .assertRaises() to verify that get_holidays() raises an exception given the new side effect of .get().

Run this test to see the result of your test:

Shell
$ python holidays.py
.
-------------------------------------------------------
Ran 1 test in 0.000s

OK

If you want to be a little more dynamic, you can set .side_effect to a function that Mock will invoke when you call your mocked method. The mock shares the arguments and return value of the .side_effect function:

Python holidays.py
import requests
import unittest
from unittest.mock import Mock

# Mock requests to control its behavior
requests = Mock()

def get_holidays():
    r = requests.get("http://localhost/api/holidays")
    if r.status_code == 200:
        return r.json()
    return None

class TestHolidays(unittest.TestCase):
    def log_request(self, url):
        # Log a fake request for test output purposes
        print(f"Making a request to {url}.")
        print("Request received!")

        # Create a new Mock to imitate a Response
        response_mock = Mock()
        response_mock.status_code = 200
        response_mock.json.return_value = {
            "12/25": "Christmas",
            "7/4": "Independence Day",
        }
        return response_mock

    def test_get_holidays_logging(self):
        # Test a successful, logged request
        requests.get.side_effect = self.log_request
        assert get_holidays()["12/25"] == "Christmas"

if __name__ == "__main__":
    unittest.main()

First, you created .log_request(), which takes a URL, logs some output using print(), then returns a Mock response. Next, you set the .side_effect of .get() to .log_request(), which you’ll use when you call get_holidays(). When you run your test, you’ll see that .get() forwards its arguments to .log_request(), then accepts the return value and returns it:

Shell
$ python holidays.py
Making a request to http://localhost/api/holidays.
Request received!
.
-------------------------------------------------------
Ran 1 test in 0.000s

OK

Great! The calls to print() logged the correct values. Also, get_holidays() returned the holidays dictionary.

.side_effect can also be an iterable. The iterable must consist of return values, exceptions, or a mixture of both. The iterable will produce its next value every time you call your mocked method. For example, you can test that a retry after a Timeout returns a successful response:

Python
import unittest
from requests.exceptions import Timeout
from unittest.mock import Mock

# Mock requests to control its behavior
requests = Mock()

def get_holidays():
    r = requests.get("http://localhost/api/holidays")
    if r.status_code == 200:
        return r.json()
    return None

class TestHolidays(unittest.TestCase):
    def test_get_holidays_retry(self):
        # Create a new Mock to imitate a Response
        response_mock = Mock()
        response_mock.status_code = 200
        response_mock.json.return_value = {
            "12/25": "Christmas",
            "7/4": "Independence Day",
        }
        # Set the side effect of .get()
        requests.get.side_effect = [Timeout, response_mock]
        # Test that the first request raises a Timeout
        with self.assertRaises(Timeout):
            get_holidays()
        # Now retry, expecting a successful response
        assert get_holidays()["12/25"] == "Christmas"
        # Finally, assert .get() was called twice
        assert requests.get.call_count == 2

if __name__ == "__main__":
    unittest.main()

When you run the updated file, you’ll see that the new test case, .test_get_holidays_retry(), passes as well:

Shell
$ python holidays.py
.
-------------------------------------------------------
Ran 1 test in 0.000s

OK

The first time you call get_holidays(), .get() raises a Timeout. The second time, the method returns a valid holidays dictionary. These side effects match the order they appear in the list passed to .side_effect.

You can set .return_value and .side_effect on a Mock directly. However, because a Python mock object needs to be flexible in creating its attributes, there’s a better way to configure these and other settings.

Configuring Your Mock

You can configure a Mock to set up some of the object’s behaviors. Some configurable members include .side_effect, .return_value, and .name. You configure a Mock when you create one or when you use .configure_mock().

You can configure a Mock by specifying certain attributes when you initialize an object:

Python
>>> from unittest.mock import Mock

>>> mock = Mock(side_effect=Exception)
>>> mock()
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "/path/to/python/unittest/mock.py", line 939, in __call__
    return _mock_self._mock_call(*args, **kwargs)
  File "/path/to/python/unittest/mock.py", line 995, in _mock_call
    raise effect
Exception

>>> mock = Mock(return_value=True)
>>> mock()
True

>>> mock = Mock(name="Real Python Mock")
>>> mock
<Mock name='Real Python Mock' id='4434041432'>

As you’ve seen in the previous section, you can set .side_effect and .return_value directly on the Mock instance itself after initialization.

Some other attributes, such as the name of your Mock, you’d primarily set when initializing the object, like shown above. If you try to set the .name of the Mock on the instance, you’ll get a different result than what you might expect:

Python
>>> mock = Mock()
>>> mock.name = "Real Python Mock"
>>> mock
<Mock id='4339891024'>
>>> mock.name
'Real Python Mock'

The name .name is a common attribute in objects. So, Mock doesn’t let you set that value on the instance in the same way you can with .return_value or .side_effect. If you access mock.name, you’ll create a .name attribute instead of configuring your mock.

In general, if you want to configure an existing Mock, you can use .configure_mock():

Python
>>> mock = Mock()
>>> mock.configure_mock(return_value=True)
>>> mock()
True

By unpacking a dictionary into either a call to .configure_mock() or Mock.__init__(), you can even configure your Python mock object’s attributes.

Using Mock configurations, you could simplify a previous example:

Python
# Verbose Mock shown before
response_mock = Mock()
response_mock.json.return_value = {
    "12/25": "Christmas",
    "7/4": "Independence Day",
}

# Concise initialization using a configuration dict
holidays = {"12/25": "Christmas", "7/4": "Independence Day"}
response_mock = Mock(**{"json.return_value": holidays})

Now, you can create and configure Python mock objects. You can also use mocks to control the behavior of your application. So far, you’ve used mocks as arguments to functions or patching objects in the same module as your tests.

Next, you’ll learn how to substitute your mocks for real objects in other modules.

The patch() Function

The unittest.mock library 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’ll mock the target object.

Using patch() as a Decorator

If you want to mock an object for the duration of your entire test function, then you can use patch() as a function decorator.

To see how this works, reorganize your holidays.py file by putting the logic and tests into separate files:

Python holidays.py
from datetime import datetime

import requests

def is_weekday():
    today = datetime.today()
    # Python's datetime library treats Monday as 0 and Sunday as 6
    return (0 <= today.weekday() < 5)

def get_holidays():
    r = requests.get("http://localhost/api/holidays")
    if r.status_code == 200:
        return r.json()
    return None

The functions of your module are now in their own file, separate from their tests. Next, you’ll re-create your tests in a file called test_holidays.py.

Up to this point, you’ve monkey patched objects in the file in which they exist. Monkey patching is the replacement of one object with another at runtime. Now, you’ll use patch() to replace your objects in holidays.py from within your test file:

Python test_holidays.py
import unittest
from unittest.mock import patch

from requests.exceptions import Timeout

from holidays import get_holidays

class TestHolidays(unittest.TestCase):
    @patch("holidays.requests")
    def test_get_holidays_timeout(self, mock_requests):
            mock_requests.get.side_effect = Timeout
            with self.assertRaises(Timeout):
                get_holidays()
                mock_requests.get.assert_called_once()

if __name__ == "__main__":
    unittest.main()

Originally, you created a Mock and patched requests in the local scope. Now, you need to access the requests library in holidays.py from tests.py.

In this case, you used patch() as a decorator and passed the target object’s path. The target path was "holidays.requests", which consists of the module name and the object.

You also defined a new parameter, mock_requests, for the test function. patch() uses this parameter to pass the mocked object into your test. From there, you can modify the mock or make assertions as necessary.

You can execute this test module to ensure it’s working as expected:

Shell
$ python test_holidays.py
.
-------------------------------------------------------
Ran 1 test in 0.001s

OK

Great! The test case passes, which means that you’ve successfully replaced the requests library in the scope of holidays with a Mock object.

Using patch() as a decorator worked well in this example. In some cases, it’s more readable or more effective to use patch() as a context manager.

Using patch() as a Context Manager

Sometimes, you’ll want to use patch() as a context manager rather than a decorator. Some reasons why you might prefer a context manager include the following:

  • You only want to mock an object for a part of the test scope.
  • You’re already using too many decorators or parameters, which hurts your test’s readability.

To use patch() as a context manager, you use Python’s with statement:

Python test_holidays.py
import unittest
from unittest.mock import patch

from requests.exceptions import Timeout

from holidays import get_holidays

class TestHolidays(unittest.TestCase):
    def test_get_holidays_timeout(self):
        with patch("holidays.requests") as mock_requests:
            mock_requests.get.side_effect = Timeout
            with self.assertRaises(Timeout):
                get_holidays()
                mock_requests.get.assert_called_once()

if __name__ == "__main__":
    unittest.main()

Using this approach, Python will mock the requests library in holidays only within the context manager. When the test exits the with statement, patch() replaces the mocked object again with the original.

Until now, you’ve mocked complete objects, but sometimes you’ll only want to mock a part of an object.

Patching an Object’s Attributes

If you only want to mock one method of an object instead of the entire object, you can do so by using patch.object().

For example, .test_get_holidays_timeout() really only needs to mock requests.get() and set its .side_effect to Timeout:

Python test_holidays.py
import unittest
from unittest.mock import patch

from holidays import get_holidays, requests

class TestHolidays(unittest.TestCase):
    @patch.object(requests, "get", side_effect=requests.exceptions.Timeout)
    def test_get_holidays_timeout(self, mock_requests):
        with self.assertRaises(requests.exceptions.Timeout):
            get_holidays()

if __name__ == "__main__":
    unittest.main()

In this example, you’ve mocked only get() rather than all of requests. Every other attribute remains the same.

object() takes the same configuration parameters that patch() does. But instead of passing the target’s path, you provide the target object itself as the first parameter. The second parameter is the attribute of the target object that you’re trying to mock. You can also use object() as a context manager like patch().

Learning how to use patch() is critical to mocking objects in other modules. However, sometimes it’s not obvious what the target object’s path is.

Knowing Where to Patch

Knowing where to tell patch() to look for the object you want mocked is important because if you choose the wrong target location, then the result of patch() could be something you didn’t expect.

Let’s say you are mocking is_weekday() in holidays.py using patch():

Python
>>> import holidays
>>> from unittest.mock import patch

>>> with patch("holidays.is_weekday"):
...     holidays.is_weekday()
...
<MagicMock name='is_weekday()' id='4336501256'>

First, you import holidays.py. Then you patch is_weekday(), replacing it with a Mock. Great! This is working as expected.

Now, you’ll do something slightly different and import the function directly:

Python
>>> from holidays import is_weekday
>>> from unittest.mock import patch

>>> with patch("holidays.is_weekday"):
...     is_weekday()
...
True

This time, the output is not a Mock like before.

Notice that even though the target location you passed to patch() didn’t change, the result of calling is_weekday() is different. The difference comes from the change in how you imported the function.

When you use from holidays import is_weekday, you bind the is_weekday() to the local scope. So, even though you patch() the function later, you ignore the mock because you already have a local reference to the unmocked function.

A good rule of thumb is to patch() the object where it’s looked up.

In the first example, mocking "holidays.is_weekday()" works because you look up the function in the holidays module. In the second example, you have a local reference to is_weekday(). Since you use the function found in the local scope, you should mock the local function:

Python
>>> from unittest.mock import patch
>>> from holidays import is_weekday

>>> with patch("__main__.is_weekday"):
...     is_weekday()
...
<MagicMock name='is_weekday()' id='4502362992'>

In this example, you’ve patched the function in the local scope that you can access through __main__. Because of this, your mock worked as expected.

Now, you have a firm grasp on the power of patch(). You’ve seen how to patch() objects and attributes as well as where to patch them. Next, you’ll see some common problems inherent in object mocking and the solutions that unittest.mock provides.

Common Mocking Problems

Mocking objects can introduce several problems into your tests. Some problems are inherent in mocking while others are specific to unittest.mock. Keep in mind that there are other issues with mocking that are not mentioned in this tutorial.

The ones covered here are similar in that the problems they cause are fundamentally the same. In each case, the test assertions are irrelevant. Though the intention of each mock is valid, the mocks themselves are not.

Changes to Object Interfaces and Misspellings

Classes and function definitions change all the time. When the interface of an object changes, any tests relying on a Mock of that object may become irrelevant.

For example, you rename a method but forget that a test mocks that method and invokes .assert_not_called(). After the change, .assert_not_called() is still True. However, the assertion isn’t useful because the method no longer exists.

Irrelevant tests may not sound critical, but if they’re your only tests and you assume that they work properly, the situation could be disastrous for your application.

A problem specific to Mock is that a misspelling can break a test. Recall that a Mock creates its interface when you access its members. So, if you misspell its name, you’ll inadvertently create a new attribute.

For example, if you misspell .assert_called() by instead typing .asst_called(), then your test won’t raise an AssertionError. This is because you’ve created a new method on the Python mock object named .asst_called() instead of evaluating an actual assertion.

The problems mentioned in this section occur when you mock objects within your own codebase. A different problem arises when you mock objects that interact with external codebases.

Changes to External Dependencies

Imagine again that your code makes a request to an external API. In this case, the external dependency is the API which is susceptible to change without your consent.

On one hand, what unit tests are there for is to test isolated components of code. So, mocking the code that makes the request helps you to test your isolated components under controlled conditions. However, it also presents a potential problem.

If an external dependency changes its interface, your Python mock objects will become invalid. If this happens and the interface change is a breaking one, your tests will pass because your mock objects have masked the change, but your production code will fail.

Unfortunately, this isn’t a problem that unittest.mock provides a solution for. You must exercise judgment when mocking external dependencies.

All of these issues can cause test irrelevancy and potentially costly issues because they threaten the integrity of your mocks. While unittest.mock can’t entirely avoid these problems, it gives you some tools for dealing with them.

Avoiding Common Problems Using Specifications

As mentioned before, if you change a class or function definition or misspell a Python mock object’s attribute, you can cause problems with your tests.

These problems occur because Mock creates attributes and methods lazily when you access them. The answer to these issues is to prevent Mock from creating attributes that don’t conform to the object you’re trying to mock.

When configuring a Mock, you can pass an object specification to the spec parameter. The spec parameter accepts a list of names or another object and defines the mock’s interface. If you attempt to access an attribute that doesn’t belong to the specification, Mock will raise an AttributeError:

Python
>>> from unittest.mock import Mock

>>> fake_holidays = Mock(spec=["is_weekday", "get_holidays"])

>>> fake_holidays.is_weekday()
<Mock name='mock.is_weekday()' id='4569015856'>

>>> fake_holidays.create_event()
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "/path/to/python/unittest/mock.py", line 653, in __getattr__
    raise AttributeError("Mock object has no attribute %r" % name)
AttributeError: Mock object has no attribute 'create_event'

Here, you’ve specified that fake_holidays has two methods called .is_weekday() and .get_holidays(). When you access .is_weekday(), it returns a Mock. However, when you access .create_event(), a method that doesn’t match the specification, then Mock raises an AttributeError.

Specifications work the same way if you configure the Mock with an object:

Python
>>> from unittest.mock import Mock
>>> import holidays

>>> fake_holidays = Mock(spec=holidays)

>>> fake_holidays.is_weekday()
<Mock name='mock.is_weekday()' id='4569435216'>

>>> fake_holidays.create_event()
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "/path/to/python/unittest/mock.py", line 653, in __getattr__
    raise AttributeError("Mock object has no attribute %r" % name)
AttributeError: Mock object has no attribute 'create_event'

The .is_weekday() method is available to your fake_holidays mock because you configured the Mock to match the interface of your holidays module.

unittest.mock also provides convenient ways to automatically specify the interface of a Mock instance.

One way to implement automatic specifications is with create_autospec():

Python
>>> from unittest.mock import create_autospec
>>> import holidays

>>> fake_holidays = create_autospec(holidays)

>>> fake_holidays.is_weekday()
<MagicMock name='mock.is_weekday()' id='4579049424'>

>>> fake_holidays.create_event()
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "/path/to/python/unittest/mock.py", line 653, in __getattr__
    raise AttributeError("Mock object has no attribute %r" % name)
AttributeError: Mock object has no attribute 'create_event'

Like before, fake_holidays is a Mock instance whose interface matches holidays. This time, you created it using create_autospec() and you’ll notice that .is_weekday() is a MagicMock instead.

If you’re using patch(), then you can set the autospec parameter to True to achieve the same result:

Python
>>> from unittest.mock import patch
>>> import holidays

>>> with patch("__main__.holidays", autospec=True) as fake_holidays:
...     fake_holidays.is_weekday()
...     fake_holidays.create_event()
...
<MagicMock name='holidays.is_weekday()' id='4579094312'>
Traceback (most recent call last):
  File "<stdin>", line 3, in <module>
  File "/path/to/python/unittest/mock.py", line 653, in __getattr__
    raise AttributeError("Mock object has no attribute %r" % name)
AttributeError: Mock object has no attribute 'create_event'

Again, your mock of holidays automatically adopted the module’s specification and set up guardrails to prevent you from accidentally creating objects that don’t match that specification.

Conclusion

In this tutorial, you’ve learned so much about mocking objects using unittest.mock!

Now, you’re able to:

  • Use Mock to imitate objects in your tests
  • Check usage data to understand how you use your objects
  • Customize your mock objects’ return values and side effects
  • Use patch() objects throughout your codebase
  • Identify and avoid problems with using Python mock objects

You’ve built a foundation of understanding that will help you build better tests. Now, you can use mocks to gain insights into your code that you wouldn’t have otherwise had.

Keep on reading about unittest and unittest.mock to build your knowledge and intuition for writing better tests in Python.

Take the Quiz: Test your knowledge with our interactive “Understanding the Python Mock Object Library” quiz. You’ll receive a score upon completion to help you track your learning progress:


Interactive Quiz

Understanding the Python Mock Object Library

In this quiz, you'll test your understanding of Python's unittest.mock library. With this knowledge, you'll be able to write robust tests, create mock objects, and ensure your code is reliable and efficient.

Watch Now This tutorial has a related video course created by the Real Python team. Watch it together with the written tutorial to deepen your understanding: Improve Your Tests With the Python Mock Object Library

🐍 Python Tricks 💌

Get a short & sweet Python Trick delivered to your inbox every couple of days. No spam ever. Unsubscribe any time. Curated by the Real Python team.

Python Tricks Dictionary Merge

About Alex Ronquillo

Alex Ronquillo is a Software Engineer at thelab. He’s an avid Pythonista who is also passionate about writing and game development.

» More about Alex

Each tutorial at Real Python is created by a team of developers so that it meets our high quality standards. The team members who worked on this tutorial are:

Master Real-World Python Skills With Unlimited Access to Real Python

Locked learning resources

Join us and get access to thousands of tutorials, hands-on video courses, and a community of expert Pythonistas:

Level Up Your Python Skills »

Master Real-World Python Skills
With Unlimited Access to Real Python

Locked learning resources

Join us and get access to thousands of tutorials, hands-on video courses, and a community of expert Pythonistas:

Level Up Your Python Skills »

What Do You Think?

Rate this article:

What’s your #1 takeaway or favorite thing you learned? How are you going to put your newfound skills to use? Leave a comment below and let us know.

Commenting Tips: The most useful comments are those written with the goal of learning from or helping out other students. Get tips for asking good questions and get answers to common questions in our support portal.


Looking for a real-time conversation? Visit the Real Python Community Chat or join the next “Office Hours” Live Q&A Session. Happy Pythoning!

Keep Learning