Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How can I mock requests and the response?

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)?

like image 545
kk1957 Avatar asked Apr 01 '13 23:04

kk1957


People also ask

What is request mocking?

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.

How do you make a mock in Python?

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 .


2 Answers

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!

like image 101
Johannes Fahrenkrug Avatar answered Sep 23 '22 19:09

Johannes Fahrenkrug


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.

like image 30
Anentropic Avatar answered Sep 24 '22 19:09

Anentropic