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.