What is mock in unittest
When writing tests using Python's unittest library, you can use mocks to write dependency-free tests. Mocking allows you to simulate interactions with external resources and other modules, increasing the reliability and speed of your tests.
The following are the basic steps for writing tests using mocks with Python's unittest library.
- Import the
unittest.mock
module
from unittest.mock import MagicMock, Mock, patch
- Import modules, functions, and classes to be tested
from my_module import my_function
- Create a test class and define test methods
import unittest
class TestMyFunction(unittest.TestCase):
def test_my_function(self):
# write test code here
- Create mock objects in test methods to replace functions and methods under test
import unittest
from unittest.mock import MagicMock
class TestMyFunction(unittest.TestCase):
def test_my_function(self):
mock_obj = MagicMock(return_value=42)
with patch('my_module.my_function', mock_obj):
result = my_function()
self.assertEqual(result, 42)
In this example, when my_module.my_function()
is called, mock_obj
is called and the value specified in return_value
is returned. Tests show that the return value of my_function()
is 42.
Mocks and patches
Mocks in Python's unittest are objects that are used in place of real objects during testing, allowing you to simulate the behavior you need during testing, even if you rely on other objects in your code. This eliminates the need to access external resources such as real databases or networks during testing. using Mock objects increases the speed of testing and improves test reliability.
Python's unittest patch is a feature that allows you to use Mock to modify the code being executed during testing. Patches are used to replace real objects during testing to simulate the required behavior. A patch may be used during a single test or shared by all tests by setting it up in a setUp
method. For example, when testing a method that calls an external API, the test execution time will increase if the API is actually called, and the test may fail if the API is offline. Therefore, a patch can be used to mock the API calls and simulate the data retrieved from the API during testing. This increases the speed of testing and ensures that the test will not fail even if the API is offline.
Term | Description |
---|---|
mock | An object that has the ability to check arguments and the number of times it is called, and can be configured to return values and send exceptions at will |
patch | Replace system functions and classes with Mock objects |
Key classes and functions in unittest.mock
The unittest.mock
package contains the following main classes and functions.
Mock
: Mock object used to replace other objectsMagicMock
: Subclass of the Mock class with magic methodspatch
: Context manager for replacing objects used in tests
Using these classes and functions, you can easily mock external objects during testing. For example, if you have a function used to read a file, you can use a mock file object to test what to do if the file does not exist.
Mock
Python's unittest module has a Mock
class for creating mock objects for unit testing. The Mock
class is an object that mocks a single attribute or method. You can set arbitrary return values for attributes or methods.
from unittest.mock import Mock
# create mock object
mock_obj = Mock()
# set attribute
mock_obj.attr = 'value'
# assert attribute
assert mock_obj.attr == 'value'
# set method
mock_obj.method.return_value = 42
# call method
result = mock_obj.method()
# verify result
assert result == 42
MagicMock
MagicMock
is a class in Python's unittest.mock
module and a subclass of the Mock
class. MagicMock
has all the features of the Mock
class, plus the ability to automatically generate attributes and methods.
Using MagicMock
saves you time when writing test code, since calling an object's attributes will automatically return another MagicMock
object. In addition, the values set on the attributes are saved and can be accessed later.
For example, you can create a MagicMock
object like this.
from unittest.mock import MagicMock
magic_mock_obj = MagicMock()
Any attribute can be set for this object.
magic_mock_obj.some_attribute = 10
In this case, the some_attribute
attribute is set to the value 10. You can also make assertions on attributes.
assert magic_mock_obj.some_attribute == 10
MagicMock
automatically creates a MagicMock
object on attribute access. For example, if you have the code magic_mock_obj.some_object.some_method()
, even if the attribute some_object
does not exist, no error will occur and a MagicMock
object will be automatically generated. This saves time when writing test code.
The MagicMock
object also has the ability to track information about the call, just like the Mock
object. For example, you can track the number of times the MagicMock
object is invoked, its arguments, etc.
Differences from Mock
In Python unittest, Mock
and MagicMock
are both tools for creating mock objects, but there are subtle differences.
Mock objects are used to mock callable functions and classes; Mock objects can make assertions on mocked objects and validate arguments passed at call time.
MagicMock, on the other hand, is a subclass of the Mock object and can also mock access to attributes. Specifically, attributes can be set and Mock objects can be automatically created when a method is invoked.
In other words, a Mock object can mock only callable objects, whereas MagicMock can mock not only callable objects but also attributes.
For example, given the following class, the Mock object can only mock the get_name
method, while MagicMock can mock not only the get_name
method, but also the name
attribute.
class Person:
def __init__(self, name):
self.name = name
def get_name(self):
return self.name
Therefore, if you need to mock an attribute, using MagicMock is appropriate.
patch
The unittest.mock
module has a patch()
function used for patching modules, classes, objects, or functions. The patch()
function returns a mocked object and can only be used within the context in which the patch was applied.
The patch()
function can also be used as a decorator. In this case, the patched object is passed as an argument to the decorated function.
The following is an example of a patch()
function. It mocks the get()
function in the requests
module so that no HTTP request is actually made.
from unittest.mock import patch
import requests
@patch('requests.get')
def test_requests(mock_get):
mock_response = 'Mock Response'
mock_get.return_value.text = mock_response
response = requests.get('https://www.example.com')
assert response.text == mock_response
assert mock_get.called
In this example, we are patching requests.get
. The patch()
function is passed the name or path of the object to be patched as an argument. In this example, the path requests.get
in string format is used to patch requests.get
.
Within the test_requests()
function, a mock object mock_get
is created and the text
attribute is set to mock_response
. Next, we set mock_get
to return_value
. This will cause the mock_response
to be returned when the requests.get()
function is called.
Finally, requests.get()
is called to verify the response text and that the called
attribute of the mock object is True
.
Mock object attributes and methods
Mock objects have various attributes and methods. The following are the main attributes and methods that a Mock object has.
return_value
The return_value
attribute of the Mock object is used to set and retrieve the value returned when the mock is invoked.
For example, suppose you have the following mock.
from unittest.mock import Mock
mock = Mock()
Use the return_value
attribute to set the value returned when this mock is called.
mock.return_value = 10
In this case, 10 is always returned when the mock is called.
assert mock() == 10
You can also use the return_value
attribute to get the value returned when the mock is called.
assert mock.return_value == 10
The return_value
attribute is used to control the value returned when the mock is called. When used in a test, it can be used to verify that the value is returned as expected. It can also be used to test a specific behavior of a program by setting the value returned when the mock is called.
side_effect
The side_effect
attribute of the Mock object is used to set the function to be executed when the mock is called.
Here is an example of a mock that uses side_effect
to throw an exception.
from unittest.mock import Mock
def func():
raise ValueError("Error")
mock = Mock(side_effect=func)
Calling this mock executes the specified function.
mock()
# ValueError: Error
The side_effect
is useful when what you return as a result of a call is not a simple value, or when you want to return different results for multiple calls. For example, suppose you have a mock with a function that returns a list as follows.
mock = Mock()
mock.side_effect = [[1, 2], [3, 4], [5, 6]]
This mock returns [1, 2]
on the first call, [3, 4]
on the second call, and always [5, 6]
on the third and subsequent calls.
assert mock() == [1, 2]
assert mock() == [3, 4]
assert mock() == [5, 6]
assert mock() == [5, 6]
The side_effect
is used to set the function that will be executed when the mock is called. When used in a test, it can be used to check if a function is executed as expected.
called
The called
attribute of a Mock object is a boolean value indicating whether or not the mock has been called. It is True
if the mock has been ## called, and False
if it has not.
For example, suppose you have the following mock.
from unittest.mock import Mock
mock = Mock()
In the state before this mock is called, the value of the called
attribute is False
.
assert mock.called == False
When this mock is called, the value of the called
attribute will be True
.
mock()
assert mock.called == True
The called
attribute can be used to verify that the mock has been called. This is useful, for example, if you need to do some processing after verifying that the mock is called.
call_args
The call_args
attribute of the Mock object is an object that contains the arguments of the last time the mock was called. This object is returned in the form of a unittest.mock.call
object.
For example, suppose we have the following mock.
from unittest.mock import Mock
mock = Mock()
mock(1, 2, 3)
In this case, the call_args
attribute can be used to get the arguments of the last time the mock was called.
assert mock.call_args == ((1, 2, 3),)
Thus, the call_args
attribute can be used to retrieve the last argument called and validate it in a test. You can also use the call_args
attribute to see how the mock was called.
The call_args
attribute can be used in conjunction with convenience assertion methods such as assert_called_with()
. It can also be used to check if the mock received arguments as expected.
call_args_list
The call_args_list
attribute of a Mock object is a list of tuples representing all arguments when the mock is called. It can be used to record all arguments passed when the mock was called.
For example, suppose we have the following mock.
from unittest.mock import Mock
mock = Mock()
If you call this mock twice, passing different arguments each time, the call_args_list
attribute will contain a tuple containing each argument.
mock('foo')
mock('bar')
assert mock.call_args_list == [('foo',), ('bar',)]
The call_args_list
attribute can be used to retrieve all arguments passed when the mock is called and validate them in tests. It also prevents the test from failing if the order or number of arguments is changed.
Note that the call_args_list
attribute stores the arguments in the order in which the mock was called, so you will get different results if they are called in a different order.
call_count
The call_count
attribute of a Mock object is an integer value that returns the number of times the mock has been called. In other words, it is used to check how many mocks have been called.
For example, suppose you have the following mock
from unittest.mock import Mock
mock = Mock()
If this mock is called three times, the call_count
attribute will contain the number of calls, 3.
mock()
mock()
mock()
assert mock.call_count == 3
The call_count
attribute can be used to get the number of times a mock has been called and verified in a test. For example, it can be used to check if a mock has been called the expected number of times.
The call_count
attribute can be used in conjunction with convenience assertion methods such as assert_called_once()
. The call count can also be monitored to ensure that the program is working properly or that an error has occurred.
mock_calls
The mock_calls
attribute of a Mock object is a list of method calls representing all the calls made when that mock was invoked. In other words, it is used to record the method name, arguments, return value, and other information when the mock was invoked.
For example, suppose we have the following mock.
from unittest.mock import Mock
mock = Mock()
If this mock is called twice, each time passing different arguments, the mock_calls
attribute will contain an object with information about each call.
mock('foo')
mock('bar')
assert mock.mock_calls == [call('foo'), call('bar')]
The mock_calls
attribute can be used to obtain information about when the mock was called and can be verified in tests. For example, it can be used to verify that the mock was called in the expected order.
The mock_calls
attribute is very useful for debugging and validating tests because it contains information such as the name of the method called, its arguments, and the return value. However, since this attribute may contain a large amount of information, it may be desirable to limit it to a reasonable number of calls.
reset_mock
The reset_mock
method is used to reset the past state of a Mock object, including whether it has been called before and the arguments and return value of the call. This is especially useful when the same Mock object is used across multiple tests.
Calling reset_mock
will reset the following attributes.
call_args
call_count
call_args_list
mock_calls
return_value
side_effect
For example, consider the following Mock object.
from unittest.mock import Mock
mock = Mock(return_value=42)
mock(1, 2, 3)
This Mock object is called once with arguments 1, 2, and 3 and returns 42 as the return value. Calling reset_mock
will reset these attributes.
mock.reset_mock()
assert mock.call_count == 0
assert mock.call_args is None
assert mock.call_args_list == []
assert mock.mock_calls == []
assert mock.return_value is None
assert mock.side_effect is None
Thus, reset_mock
can be used to reuse Mock objects between tests.
assert_called
The assert_called()
method is used to assert that a Mock object has been called at least once. This method only works if the Mock object is called like a normal function or method.
For example, consider the following Mock object.
from unittest.mock import Mock
mock_obj = Mock()
To assert that this Mock object has been called one or more times, do the following.
# assert that the mock object has been called at least once
mock_obj.assert_called()
This method raises an exception if the Mock object is not called. Therefore, the Mock object must be called at least once before asserting the call. Alternatively, the assert_called_once()
method can be used if the call must be made once.
assert_has_calls
The has_assert_calls()
method is used to assert whether the Mock object has received the expected calls. This method can be one or more call
objects representing the expected calls.
This method can be used as follows.
from unittest.mock import Mock, call
mock_obj = Mock()
mock_obj(1, 2, 3)
mock_obj('a', 'b', 'c')
# asserts that the mock object has been called in the specified order
mock_obj.assert_has_calls([call(1, 2, 3), call('a', 'b', 'c')])
mock_obj.has_assert_calls([call(1, 2, 3), call('a', 'b', 'c')])
The above example asserts that the Mock object mock_obj
has received two calls, call(1, 2, 3)
and call('a', 'b', 'c')
. The assert_has_calls()
method also checks that the calls are in the specified order.
assert_called_once
assert_called_once
is a method that asserts that a Mock object has been called only once. Before calling assert_called_once
, you can also call assert_called
or assert_called_with
to verify that the Mock object was called only once.
from unittest.mock import MagicMock
mock_obj = MagicMock()
mock_obj()
mock_obj.assert_called_once()
assert_called_with
assert_called_with
is a method that verifies that a Mock object has been called with the specified arguments. The test will pass only if the called argument matches the expected value.
The following is an example of using assert_called_with
.
from unittest.mock import MagicMock
def test_example():
mock_obj = MagicMock()
mock_obj('foo', 'bar', baz='bazval')
mock_obj.assert_called_with('foo', 'bar', baz='bazval')
This test creates a mock object and calls it with the arguments 'foo'
and 'bar'
and the keyword argument baz='bazval'
. The assert_called_with
method is then used to verify that mock was called with the correct arguments.
Thus, the assert_called_with
method can be used to easily verify that the mock was called with the correct arguments. The assert_called_with
method can also be used to simultaneously verify information such as the number of calls.
Mock examples
Here are some examples of using Mock
.
Mock a function in a module
The following is an example of mocking the get
function of the requests
module.
import requests
from unittest.mock import Mock
def test_fetch_data():
mock_response = Mock()
mock_response.status_code = 200
mock_response.json.return_value = {"foo": "bar"}
requests.get = Mock(return_value=mock_response)
result = fetch_data()
assert result == {"foo": "bar"}
In this example, we mock the requests.get
function and set the return value to a Mock object, which has a status_code
attribute and a json() method. The json()
method is set to return a Mock
object by the return_value
attribute.
Mock methods of a class
The following is an example of mocking the add
method of the Calculator
class.
from unittest.mock import Mock
class Calculator:
def add(self, x, y):
return x + y
def test_calculator():
# mock the get function in the requests module
calculator = Calculator()
# mock the add method of the Calculator class
calculator.add = Mock(return_value=10)
# run test
result = calculator.add(1, 2)
assert result == 10
This example mocks the add
method of the Calculator
class and sets the return value to 10
.
Test for exceptions using mock objects
The following is an example of testing for exceptions using a mock object.
from unittest.mock import Mock
def test_divide_by_zero():
mock_logger = Mock()
import logger
logger.error = mock_logger
try:
1 / 0
except ZeroDivisionError as e:
mock_logger.assert_called_with("division by zero")
In this example, we mock the error
function of the logger
module to test that a ZeroDivisionError
exception raised by a zero division causes the logger.error
function to be called.
MagicMock example
The following is a simple example using MagicMock
. As an example, we have code that calls the get()
function of the requests
module. This example mocks the response that is returned when the get()
function is called.
import requests
from unittest.mock import MagicMock
def test_requests():
response_mock = MagicMock()
response_mock.status_code = 200
response_mock.json.return_value = {'key': 'value'}
requests.get = MagicMock(return_value=response_mock)
response = requests.get('https://www.example.com')
assert response.status_code == 200
assert response.json()['key'] == 'value'
This example creates a response_mock
and sets its status_code
attribute. It also mocks the json()
method and sets its return value. Next, we replace requests.get
with MagicMock
and set return_value
to response_mock
. Finally, requests.get('https://www.example.com')
is called to verify the response status code and JSON value.
patch examples
Here are some examples of using patch
.
Mock a function in an external module
For example, suppose you have a function that uses the following math
module.
import math
def calculate_square_root(num):
return math.sqrt(num)
Within this calculate_square_root
function, you can mock the math.sqrt
function. Here is an example of mocking math.sqrt
using patch
.
import unittest
from unittest.mock import patch
from my_module import calculate_square_root
class TestCalculateSquareRoot(unittest.TestCase):
@patch('my_module.math.sqrt')
def test_calculate_square_root(self, mock_sqrt):
mock_sqrt.return_value = 2.0
result = calculate_square_root(4)
self.assertEqual(result, 2.0)
The above example uses the @patch('my_module.math.sqrt')
decorator to mock math.sqrt
. We also set mock_sqrt.return_value
to set the return value of the mock.
Mock an object's attributes
Suppose you have the following class.
class MyClass:
def __init__(self):
self.my_attribute = 42
def my_method(self):
return self.my_attribute
You can mock the my_attribute
attribute of an instance of this class. The following is an example of mocking my_attribute
attribute using patch
.
import unittest
from unittest.mock import patch
from my_module import MyClass
class TestMyClass(unittest.TestCase):
@patch('my_module.MyClass.my_attribute', new=50)
def test_my_method(self):
my_instance = MyClass()
result = my_instance.my_method()
self.assertEqual(result, 50)
The above example uses the @patch('my_module.MyClass.my_attribute', new=50)
decorator to mock the my_attribute
attribute.
Mock file opening
The following is an example of a function that reads a file.
def read_file(filename):
with open(filename, 'r') as f:
return f.read()
To test this function, a mock file object can be used to test if the file does not exist. To do this, use the patch
function to replace the file and set it to return a mock file object.
from unittest.mock import patch
def test_read_file():
with patch('builtins.open', return_value=Mock(spec=open)) as mock_file:
mock_file.return_value.__enter__.return_value.read.return_value = 'test file contents'
assert read_file('test.txt') == 'test file contents'
In this example, builtins.open
is replaced with a mock object. The mock_file.return_value
represents the object returned by the open
function. The mock file object returns a file object in the __enter__
method and the contents of the file in the read
method. You can replace methods of a mock file object in the patch
context.
References