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.
Get Your Code: Click here to download the free sample code that you’ll use to learn about Python’s mock object library.
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 LibraryIn 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.
Note: The standard library includes unittest.mock
starting from Python 3.3 and in all newer versions. If you’re using an older version of Python, then you’ll need to install the official backport of the library.
To do so, install mock
from the Python Package Index (PyPI) using pip
:
$ python -m pip install mock
You may want to create and activate a virtual environment before installing the package.
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:
>>> 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:
# 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 :
>>> 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:
>>> json = Mock()
>>> json.dumps()
<Mock name='mock.dumps()' id='4392249776'>
Notice two key characteristics of this mocked version of dumps()
:
-
Unlike the real
dumps()
, this mocked method requires no arguments. In fact, it will accept any arguments that you pass to it. -
The return value of
.dumps()
is also aMock
.
The capability of Mock
to recursively define other mocks allows for you to use mocks in complex situations:
>>> 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:
- Assertions allow you to assert that you used an object as expected.
- 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()
:
>>> 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:
>>> 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()
:
>>> 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:
>>> 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:
>>> 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:
>>> # 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:
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:
$ 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
:
$ 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:
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.
Note: Though mocking datetime
like this is a good practice example for using Mock
, a fantastic library already exists for mocking datetime
called freezegun
.
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
:
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:
$ 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
:
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:
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:
$ 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:
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:
$ 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:
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:
$ 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:
>>> 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:
>>> 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.
Note: To avoid name clashes, Mock
mangles some names, such as name
, before saving them on the instance. You can access and edit that attribute on the instance via ._mock_name
.
However, note that the leading underscore signals that it’s a non-public attribute and, therefore, not intended for you to directly access and edit.
In general, if you want to configure an existing Mock
, you can use .configure_mock()
:
>>> 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:
# 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:
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:
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:
$ 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.
Note: The patch()
function returns an instance of MagicMock
, which is a Mock
subclass. MagicMock
is useful because it implements most magic methods for you, such as .__len__()
, .__str__()
, and .__iter__()
, with reasonable defaults.
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:
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
:
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()
.
Note: Besides objects and attributes, you can also patch()
dictionaries with patch.dict()
.
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()
:
>>> 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:
>>> 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.
Note: Depending on what day you’re reading this tutorial, your console output may read True
or False
.
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:
>>> 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.
Note: Interestingly, the developers of unittest.mock
handle a list of common misspellings of assert
.
If you try to access an attribute that starts with, for example, assret
, then Mock
will automatically raise an AttributeError
with an informative error message pointing out that you probably misspelled assert.
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
:
>>> 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:
>>> 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()
:
>>> 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:
>>> 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.
Disclaimer: Beware of overusing mocks! It’s easy to take advantage of the power of Python mock objects. If you mock too much, then you may decrease the value of your tests.
Keep on reading about unittest
and unittest.mock
to build your knowledge and intuition for writing better tests in Python.
Get Your Code: Click here to download the free sample code that you’ll use to learn about Python’s mock object library.
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 LibraryIn 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