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.activatedeftest_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'}
assertlen(responses.calls) == 1assert 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.mockasyncdeftest_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'))
asyncwith 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.activatedeftest_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
deftest_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
classDataProcessorTestCase(TestCase): @patch('path.to.views.requests.get')deftest_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'})