Mock vs MagicMock in Python
In Python’s unittest.mock
module, two prominent classes used for creating mock objects are Mock
and MagicMock
.
The Mock
class offers a basic foundation, allowing you to craft a mock object that can imitate virtually any other object.
MagicMock
, on the other hand, is an enriched subclass of Mock
.
Beyond inheriting all capabilities of Mock
, it predefines most of Python’s “magic” or “dunder” methods (like __getitem__
, __setitem__
, __iter__
, and more).
In a nutshell, while Mock
provides essential mocking functionalities, MagicMock
elevates them to seamlessly emulate more intricate Python behaviours.
The pytest-mock
plugin is built on top of the unittest.mock
module.
So, by default, pytest-mock
uses MagicMock
.
What is the Difference Between Mock and Patch?
- Mock: A
Mock
is a standalone object used to simulate the behaviour of functions or objects. It allows you to set up specific behaviours for method calls, like returning values or raising exceptions.
from unittest.mock import Mock
mock_obj = Mock()
mock_obj.some_method.return_value = 42
result = mock_obj.some_method()
assert result == 42
- Patch:
patch
is a context manager or decorator that temporarily replaces a function or object with a mock during a test. It’s particularly handy for mocking external dependencies.
from unittest.mock import patch
def external_function():
# Some external service call
pass
@patch('module_name.external_function')
def test_function(mock_external):
mock_external.return_value = "Mocked data"
result = external_function()
assert result == "Mocked data"
In summary, Mock
creates individual mock objects, while patch
temporarily replaces real objects/functions with mocks during tests, helping isolate your code and control its behaviour.
Mocking exceptions
def test_remove_directory_directory_not_found_exception(mocker):
"""
Test that a FileNotFoundError is raised when attempting to remove a directory that does not exist.
"""
# Arrange
mocker.patch("shutil.rmtree", side_effect=FileNotFoundError)
# Assert
with pytest.raises(FileNotFoundError):
remove_directory("/tmp/test")
We can mock exceptions too, just mock the corrensponding file.