Image by the author | DALLE-3 and Canva
Testing software is crucial to ensure reliability and functionality in different scenarios. However, if the code implementation depends on external services, it becomes a big challenge. This is where simulation comes into play. Python's simulation library provides tools to create mock objects that replace real objects, making testing easier to maintain. Simulation facilitates focused testing of components and faster test cycles.
What is mockery?
Mocking is a technique used in software testing to simulate real objects. Real objects are replaced by mock objects to simulate their functionality, allowing code to be tested in different scenarios and in isolation. Mocking is especially useful for testing specific parts of the codebase without relying on interaction with external systems, databases, or other complex services.
Let me explain this concept with an example. Let’s say you have a web application that uses an external API to retrieve data. To perform testing without depending on the real API, you can create a mock object that mimics the API responses. This way, you can test your application’s functionality without depending on the real API, which may be slow, unreliable, or even unavailable during development.
Sounds interesting, right? Let's now see how to use this library in detail.
Step-by-step guide to using Mock
Step 1: Import the mock library
He unittest.mock
is the Python standard library (3.3 and all newer versions) that provides mock objects to control the behavior of real objects. You must first import it unittest.mock
library.
from unittest.mock import Mock, patch
Step 2: Creating a dummy object
Creating a mock object is easy. Once imported, you can instantiate a mock object as follows:
Now, my_mock
is a mock object that you can configure to simulate the behavior of a real object.
Step 3: Set return values
The Mock library offers several ways to configure mock objects and control their behavior. For example, you can specify what a method should return when called:
my_mock.some_method.return_value="Hello, World!"
print(my_mock.some_method())
Production:
Step 4: Establish the side effects
Side effects are additional actions or behaviors that are triggered when a method of a mock object is called, such as raising exceptions or executing functions. In addition to return values, you can also define attributes or specify side effects like this:
def raise_exception():
raise ValueError("An error occurred")
my_mock.some_method.side_effect = raise_exception
# This will raise a ValueError
try:
my_mock.some_method()
except ValueError as e:
print(e)
Production:
In this example, ValueError
increases whenever some_method()
is called.
Step 5: Call Confirmation
Verifying method calls is essential for thorough testing. You can use assertions to specify whether a method was called, when, and with what arguments.
my_mock.calculate_length('foo', 'bar')
my_mock.calculate_length.assert_called()
my_mock.calculate_length.assert_called_once()
my_mock.calculate_length.assert_called_with('foo', 'bar')
my_mock.calculate_length.assert_called_once_with('foo', 'bar')
assert_called()
: Returns True if calculate_length was called at least onceassert_called_once()
: Returns True if calculate_length was called exactly onceassert_called_with('foo', 'bar')
: Returns True if calculate_length was called with the same argumentsassert_called_once_with('foo', 'bar')
: Returns True if calculate_length was called exactly once with the same arguments
If any of these statements fail on the mock object, a AssertionError
An error will be generated indicating that the expected behavior does not match the actual behavior of the simulation.
Step 6: Using the patch
He patch
The feature allows replacing real objects with mock objects during testing. As mentioned above, this is particularly useful for mocking third-party libraries or APIs, ensuring that tests remain isolated from real implementations. To demonstrate patching, consider the following example function that fetches data from the URL.
# my_module.py
import requests
def fetch_data(url):
response = requests.get(url)
return response.json()
You can avoid making the dream come true HTTP
requests by patching 'requests.get' like this:
# test_my_module.py
import unittest
from unittest.mock import patch
import my_module
class TestFetchData(unittest.TestCase):
@patch('my_module.requests.get')
def test_fetch_data(self, mock_get):
# Set up the mock to return a specific response
mock_get.return_value.json.return_value = {'key': 'value'}
# Call the function to test
result = my_module.fetch_data('http://example.com')
# Check the result
self.assertEqual(result, {'key': 'value'})
# Verify that requests.get was called correctly
mock_get.assert_called_once_with('http://example.com')
if __name__ == '__main__':
unittest.main()
The patch decorator is added just above the test_fetch_data function
To replace the requests.get
function with a simulation.
Step 7: Class simulation
You can mock entire classes and their methods to simulate interactions between objects. For example, you can mock a database class to test your application's interaction with the database without the need to set up an actual database connection, as shown below:
# database.py
class Database:
def connect(self):
pass
def save_user(self, user):
pass
def get_user(self, user_id):
pass
# test_database.py
from unittest.mock import Mock
# Creating a mock database object
mock_db = Mock(spec=Database)
# Simulating method calls
mock_db.connect()
mock_db.save_user({"id": 1, "name": "Alice"})
mock_db.get_user(1)
# Verifying that the methods were called
mock_db.connect.assert_called_once()
mock_db.save_user.assert_called_once_with({"id": 1, "name": "Alice"})
mock_db.get_user.assert_called_once_with(1)
Ending
That's all for today's article on unittest.mock
a powerful library for testing in Python. It allows developers to test code, ensuring smooth interactions between objects. With advanced features such as specifying side effects, confirming calls, mocking classes, and using context managers, testing various scenarios becomes easier. Start using mocks in your testing today to ensure higher quality code and smoother deployments.
Kanwal Mehreen Kanwal is a machine learning engineer and technical writer with a deep passion for data science and the intersection of ai with medicine. She is the co-author of the eBook “Maximizing Productivity with ChatGPT.” As a Google Generation Scholar 2022 for APAC, she champions diversity and academic excellence. She is also recognized as a Teradata Diversity in tech Scholar, Mitacs Globalink Research Scholar, and Harvard WeCode Scholar. Kanwal is an ardent advocate for change and founded FEMCodes to empower women in STEM fields.