Using pytest-mock to Mock Functions and Objects in Python Tests
This article introduces the pytest-mock plugin, explains when and how to use it for mocking functions, classes, and external services in Python tests, and provides a range of basic to advanced code examples along with important considerations and best‑practice tips.
pytest-mock is a powerful pytest plugin that simplifies creating mock objects for functions, classes, and methods, making it especially useful for API automation testing where external dependencies need to be simulated.
Use case : When an interface test depends on an external service, pytest-mock can mock that service so the test runs independently.
Example code
# app.py
import requests
def get_data_from_service():
response = requests.get('https://example.com/api/data')
return response.json()
def process_data(data):
# Ensure data['value'] is greater than 10
if data['value'] > 10:
return True
return FalseTest code
# test_app.py
import pytest
from app import process_data
def test_process_data(mocker):
# Mock the get_data_from_service function's return value
mocker.patch('app.get_data_from_service', return_value={'value': 20})
# Test process_data function
assert process_data(get_data_from_service()) is TrueKey considerations
Namespace : Ensure the patch path matches the actual module where the target is defined.
Scope : Mocks are scoped to the test function by default; use mocker.patch.object or specify scope for finer control.
Cleanup : pytest-mock automatically cleans up mocks after each test.
Side effects : Verify that mocked functions do not introduce unintended side effects.
Mock attributes : Use mocker.Mock or mocker.NonCallableMock to create objects with specific attributes.
Running the test
$ pytest test_app.py
============================= test session starts ==============================
platform linux -- Python 3.10.6, pytest-7.2.0, pluggy-1.0.0
rootdir: /path/to/project
collected 1 item
test_app.py . [100%]
============================== 1 passed in 0.01s ==============================Advanced examples
1. Mocking classes and objects
import pytest
from app import MyClass
class TestMyClass:
def test_method(self, mocker):
# Create a mock class instance
mock_instance = mocker.Mock()
mock_instance.method.return_value = "mocked_value"
# Patch MyClass to return the mock instance
mocker.patch('app.MyClass', return_value=mock_instance)
my_instance = MyClass()
result = my_instance.method()
assert result == "mocked_value"2. Mocking modules and packages
import pytest
from app import module
class TestModule:
def test_module_functions(self, mocker):
mocked_module = {
'func1': mocker.Mock(return_value="mocked_func1"),
'func2': mocker.Mock(return_value="mocked_func2")
}
mocker.patch.dict('app.module.__dict__', mocked_module)
assert module.func1() == "mocked_func1"
assert module.func2() == "mocked_func2"3. Mocking external libraries or third‑party services
import pytest
import requests
from app import get_data_from_service
class TestGetData:
def test_get_data(self, mocker):
mocker.patch('requests.get', return_value=mocker.Mock(json=lambda: {"value": 20}))
data = get_data_from_service()
assert data["value"] == 204. Mocking asynchronous functions
import pytest
import asyncio
from app import async_get_data_from_service
class TestAsyncGetData:
async def test_async_get_data(self, mocker):
mock_response = mocker.AsyncMock()
mock_response.json.return_value = {"value": 20}
mocker.patch('app.async_get_data_from_service', new=mock_response)
data = await async_get_data_from_service()
assert data["value"] == 205. Mocking exception raising
import pytest
from app import get_data_from_service
class TestGetData:
def test_get_data_exception(self, mocker):
mocker.patch('requests.get', side_effect=requests.exceptions.RequestException)
with pytest.raises(requests.exceptions.RequestException):
get_data_from_service()6. Mocking multiple return values
import pytest
from app import get_data_from_service
class TestGetData:
def test_get_data_multiple_values(self, mocker):
mocker.patch('requests.get', side_effect=[{'value': 20}, {'value': 30}])
data1 = get_data_from_service()
data2 = get_data_from_service()
assert data1["value"] == 20
assert data2["value"] == 307. Mocking properties
import pytest
from app import MyClass
class TestMyClass:
def test_property(self, mocker):
mock_property = mocker.PropertyMock(return_value="mocked_value")
mocker.patch('app.MyClass.property', new_callable=mock_property)
my_instance = MyClass()
assert my_instance.property == "mocked_value"8. Mocking authentication mechanisms of external services
import pytest
from app import authenticate_and_get_data
class TestAuthentication:
def test_authenticate_and_get_data(self, mocker):
mocker.patch('app.authenticate', return_value=True)
data = authenticate_and_get_data()
assert data["value"] == 20Additional notes
Ensure the mock path is correct for the target function.
Maintain proper test scope to avoid affecting other tests.
pytest-mock automatically restores mocks after each test, but manual mocks should be reset with mocker.resetall() or mocker.restore() if needed.
Document mock usage clearly to aid future maintenance.
Test Development Learning Exchange
Test Development Learning Exchange
How this landed with the community
Was this worth your time?
0 Comments
Thoughtful readers leave field notes, pushback, and hard-won operational detail here.