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'})