Mocking and Fixtures in Python (87/100 Days of Python)
Mocking and fixtures are two important concepts in Python testing that can help simplify and streamline the testing process. In this tutorial, we’ll cover the basics of mocking and fixtures and show you how to use them in your own tests.
What is Mocking?
Mocking is the process of creating simulated objects, or “mocks”, that mimic the behavior of real objects in your program. Mocks are often used in testing to simulate the behavior of external dependencies, such as a database or a web service, so that your tests can run independently of these dependencies.
In Python, you can use the unittest.mock
module to create mock objects. Mock objects can be configured to return specific values, raise specific exceptions, or have specific attributes, which makes them highly customizable and adaptable to your testing needs.
What are Fixtures?
Fixtures are objects that are created before tests are run and can be reused across multiple tests. Fixtures can be used to set up the environment for testing, such as creating a database connection or setting up a test server.
In Python, you can use the pytest
testing framework to define fixtures. Fixtures are defined using the @pytest.fixture
decorator and can be parameterized to accept arguments that are passed in from tests.
How to Use Mocking and Fixtures in Python Tests
To use mocking and fixtures in your tests, you’ll first need to install the necessary packages. You can install the pytest
testing framework and the unittest.mock
module using pip
:
pip install pytest
pip install pytest-mock
Once you’ve installed the necessary packages, you can define fixtures in your tests using the @pytest.fixture
decorator. For example, here's a fixture that creates a temporary file for testing:
import pytest
import tempfile
@pytest.fixture
def temp_file():
""" Create a temporary file for testing """
with tempfile.NamedTemporaryFile() as file:
yield file.name
In this example, the temp_file
fixture creates a temporary file using the tempfile.NamedTemporaryFile
function and returns the file's name. The yield
keyword is used to indicate that the fixture is a generator function that will be run before and after each test that uses it. You can then use the temp_file
fixture in your tests like this:
def test_write_to_file(temp_file):
""" Test writing to a temporary file """
with open(temp_file, 'w') as file:
file.write('Hello, world!')
with open(temp_file) as file:
assert file.read() == 'Hello, world!'
In this example, the test_write_to_file
function uses the temp_file
fixture as an argument. The fixture creates a temporary file, which is then used to test writing and reading data to and from the file.
You can also use the pytest-mock
package to create mock objects in your tests. For example, here's a test that uses a mock to simulate the behavior of an external dependency:
from unittest.mock import MagicMock
import requests
def download_data():
""" Download data from an external service """
response = requests.get('https://google.com')
return response.json()['data']
def test_download_data(mocker):
""" Test downloading data from an external service """
# Create a mock object for the requests library
mock_requests = MagicMock()
mocker.patch('requests.get', mock_requests)
# Set the mock object to return a specific response
response = {'data': 'Hello, world!'}
mock_requests.return_value.json.return_value = response
# Call the function that downloads data from the external service
data = download_data()
# Verify that the function returns the expected data
print('data:', data)
print(response)
assert data == response['data']
In this example, the test_download_data
function uses the mocker
fixture provided by the pytest-mock
package to create a mock object for the requests
library. The mocker.patch
method is used to patch the requests.get
method with the mock object. This ensures that when the download_data
function calls requests.get
, it will use the mock object instead of the real requests
library.
The mock object is then configured to return a specific response when the json
method is called. This allows the test to simulate the behavior of the external service without actually making a network request.
Finally, the download_data
function is called and the result is compared to the expected response.
What’s next?
- If you found this story valuable, please consider clapping multiple times (this really helps a lot!)
- Hands-on Practice: Free Python Course
- Full series: 100 Days of Python
- Previous topic: Test Coverage in Python with Pytest
- Next topic: Web Scraping with Scrapy