I am trying to use Pythons mock package to mock Pythons requests
module. What are the basic calls to get me working in below scenario?
In my views.py, I have a function that makes variety of requests.get() calls with different response each time
def myview(request): res1 = requests.get('aurl') res2 = request.get('burl') res3 = request.get('curl')
In my test class I want to do something like this but cannot figure out exact method calls
Step 1:
# Mock the requests module # when mockedRequests.get('aurl') is called then return 'a response' # when mockedRequests.get('burl') is called then return 'b response' # when mockedRequests.get('curl') is called then return 'c response'
Step 2:
Call my view
Step 3:
verify response contains 'a response', 'b response' , 'c response'
How can I complete Step 1 (mocking the requests module)?
requests-mock creates a custom adapter that allows you to predefine responses when certain URIs are called. There are then a number of methods provided to get the adapter used.
If you want to mock an object for the duration of your entire test function, you can use patch() as a function decorator. These functions are now in their own file, separate from their tests. Next, you'll re-create your tests in a file called tests.py .
This is how you can do it (you can run this file as-is):
import requests import unittest from unittest import mock # This is the class we want to test class MyGreatClass: def fetch_json(self, url): response = requests.get(url) return response.json() # This method will be used by the mock to replace requests.get def mocked_requests_get(*args, **kwargs): class MockResponse: def __init__(self, json_data, status_code): self.json_data = json_data self.status_code = status_code def json(self): return self.json_data if args[0] == 'http://someurl.com/test.json': return MockResponse({"key1": "value1"}, 200) elif args[0] == 'http://someotherurl.com/anothertest.json': return MockResponse({"key2": "value2"}, 200) return MockResponse(None, 404) # Our test case class class MyGreatClassTestCase(unittest.TestCase): # We patch 'requests.get' with our own method. The mock object is passed in to our test case method. @mock.patch('requests.get', side_effect=mocked_requests_get) def test_fetch(self, mock_get): # Assert requests.get calls mgc = MyGreatClass() json_data = mgc.fetch_json('http://someurl.com/test.json') self.assertEqual(json_data, {"key1": "value1"}) json_data = mgc.fetch_json('http://someotherurl.com/anothertest.json') self.assertEqual(json_data, {"key2": "value2"}) json_data = mgc.fetch_json('http://nonexistenturl.com/cantfindme.json') self.assertIsNone(json_data) # We can even assert that our mocked method was called with the right parameters self.assertIn(mock.call('http://someurl.com/test.json'), mock_get.call_args_list) self.assertIn(mock.call('http://someotherurl.com/anothertest.json'), mock_get.call_args_list) self.assertEqual(len(mock_get.call_args_list), 3) if __name__ == '__main__': unittest.main()
Important Note: If your MyGreatClass
class lives in a different package, say my.great.package
, you have to mock my.great.package.requests.get
instead of just 'request.get'. In that case your test case would look like this:
import unittest from unittest import mock from my.great.package import MyGreatClass # This method will be used by the mock to replace requests.get def mocked_requests_get(*args, **kwargs): # Same as above class MyGreatClassTestCase(unittest.TestCase): # Now we must patch 'my.great.package.requests.get' @mock.patch('my.great.package.requests.get', side_effect=mocked_requests_get) def test_fetch(self, mock_get): # Same as above if __name__ == '__main__': unittest.main()
Enjoy!
Try using the responses library. Here is an example from their documentation:
import responses import requests @responses.activate def test_simple(): responses.add(responses.GET, 'http://twitter.com/api/1/foobar', json={'error': 'not found'}, status=404) resp = requests.get('http://twitter.com/api/1/foobar') assert resp.json() == {"error": "not found"} assert len(responses.calls) == 1 assert responses.calls[0].request.url == 'http://twitter.com/api/1/foobar' assert responses.calls[0].response.text == '{"error": "not found"}'
It provides quite a nice convenience over setting up all the mocking yourself.
There's also HTTPretty:
It's not specific to requests
library, more powerful in some ways though I found it doesn't lend itself so well to inspecting the requests that it intercepted, which responses
does quite easily
There's also httmock.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With