Explain Codes LogoExplain Codes Logo

How can I mock requests and the response?

python
mocking
testing
responses
Anton ShumikhinbyAnton Shumikhin·Nov 27, 2024
TLDR

Let's start with an easy and engaging way to stub out the requests using the requests-mock. This gem allows creating pre-determined responses right within your tests:

import requests from requests_mock import Adapter session = requests.Session() adapter = Adapter() session.mount('http://', adapter) # Here's your magic trick! The mock is in place. adapter.register_uri('GET', 'http://example.com', text='mocked content') response = session.get('http://example.com') # Ah, smells just like 'mocked content' in the morning! assert response.text == 'mocked content'

In the above snippet, we create an obedient requests.Session and strap a requests_mock.Adapter onto it. The result? Any GET request to 'http://example.com' returns 'mocked content'. The network never gets a peek. No real service is bothered, making your tests swift and dependable.

Handling requests alternatives

If you're looking for a more elite trope with fancy features, ponder over responses. It supplies a simple and efficient way to map pre-defined responses to the requests sent by your application:

import responses import requests @responses.activate def test_simple(): # Hey presto, we have a response lined up! responses.add(responses.GET, 'http://example.com', json={'key': 'value'}, status=200) resp = requests.get('http://example.com') # Did we get what we asked for? Let's check! assert resp.json() == {'key': 'value'} assert len(responses.calls) == 1 assert responses.calls[0].request.url == 'http://example.com' assert responses.calls[0].response.text == '{"key": "value"}'

Rise of async: httpx and respx

When your app runs on asynchronous steroids, httpx and respx form a dynamic duo for mocking HTTP requests.

import httpx import respx from httpx import Response @respx.mock async def test_httpx_async(): # And just how do you like your test? "Async", please! respx.get("https://test.org/").mock(return_value=Response(200, content=b'Test')) async with httpx.AsyncClient() as client: response = await client.get("https://test.org/") # Our async test ran just 'async' we expected! assert response.content == b'Test'

Crafting your mocks

Now, let's explore advanced use cases with meticulous mocking examples:

Testing multiple URLs

To handle requests to multiple URLs or using various HTTP methods (GET, POST, etc.), the ideal setup is HTTPretty.

import httpretty import requests @httpretty.activate def test_requests(): # Two URLs, two responses, one mock. Efficiency at its best! httpretty.register_uri(httpretty.GET, 'http://example1.com', 'Response 1') httpretty.register_uri(httpretty.POST, 'http://example2.com', 'Response 2') response1 = requests.get('http://example1.com') response2 = requests.post('http://example2.com') # One Mock to rule them all! assert response1.text == 'Response 1' assert response2.text == 'Response 2'

Unit testing with mocking

Here's how you use unittest.mock.patch to replace the actual calls with mocks for unit testing:

from unittest.mock import patch from my_module import my_function def test_my_function(): with patch('my_module.requests.get') as mock_get: # A spoonful of mock makes the unit test go down, in the most delightful way! mock_get.return_value.json.return_value = {'data': 'test'} result = my_function() assert result == 'test'

Mocking Django views

Remember to separate your tests from the main code to boost clarity and maintainability. Here's an example of testing a piece of a Django view:

from django.test import TestCase from unittest.mock import patch from .views import data_processor class DataProcessorTestCase(TestCase): @patch('path.to.views.requests.get') def test_data_processor(self, mock_get): # Django + Mocking. A match made in testing heaven! mock_get.return_value.json.return_value = {'key':'value'} self.assertEqual(data_processor(), {'key': 'value'})