How can I mock requests and the response?
Here is what worked for me:
import mock
@mock.patch('requests.get', mock.Mock(side_effect = lambda k:{'aurl': 'a response', 'burl' : 'b response'}.get(k, 'unhandled request %s'%k)))
How to mock a http request post in python unittest
See Where to patch. And, you should provide a mock return value for the mock_post
using return_value
- The value returned when the mock is called.
E.g.
member.py
:
import requests
import json
def get_session_token(organization_id):
endpoint = 'http://localhost:3000/api'
response = requests.post(
endpoint + "/some/url/part",
data=json.dumps({
"Login": 'python',
"Password": '123456',
}),
headers={"Accept": "application/json"}
)
if response.status_code != 201:
raise ValueError()
return response.json()
def member(organization_id):
session_key = get_session_token(organization_id)
return session_key
test_member.py
:
from unittest.mock import patch
import unittest
import json
from member import member
class TestMember(unittest.TestCase):
@patch('member.requests.post')
def test_member_success(self, mock_post):
mock_post.return_value.status_code = 201
mock_post.return_value.json.return_value = 'mock response'
actual = member(1)
self.assertEqual(actual, 'mock response')
mock_post.assert_called_once_with(
'http://localhost:3000/api/some/url/part',
data=json.dumps({
"Login": 'python',
"Password": '123456',
}),
headers={"Accept": "application/json"}
)
@patch('member.requests.post')
def test_member_success(self, mock_post):
mock_post.return_value.status_code = 400
self.assertRaises(ValueError, member, 1)
if __name__ == '__main__':
unittest.main()
Test result:
.
----------------------------------------------------------------------
Ran 1 test in 0.001s
OK
Name Stmts Miss Cover Missing
-------------------------------------------------------------------------
src/stackoverflow/70098351/member.py 11 2 82% 18, 23
src/stackoverflow/70098351/test_member.py 11 0 100%
-------------------------------------------------------------------------
TOTAL 22 2 91%
How to mock data as request.Response type in python
You could do this:
@patch.object(requests, 'post')
def test_service_post(mock_request_post):
data = {'number': 0000, 'user_id': 0, 'name': 'john'}
def res():
r = requests.Response()
r.status_code = 200
def json_func():
return data
r.json = json_func
return r
mock_request_post.return_value = res()
assert data == service_post(data)
Test then passed for me when I ran it locally. Be aware that Mock is a mini-smell.
I used to be a big fan of Mock
. As I've grown as a dev, though, I really try to avoid it. It can trick you into some really bad design, and they can be really hard to maintain (especially since you're modifying your Mock
to hold return values). Mock
can also create a false sense of security (your test will continue to pass even if the web services changes dramatically, so you might explode in prod). I don't think you really need it here. Two alternatives:
- You could hit whatever service you're trying to hit, and serialize (save) that response out with
pickle
, and store to disk (save it in your test suite). Then have your unit test read it back in and use the actual response object. You'd still have topatch
overrequests.post
, but at least the return values will be lined up for you and you won't have to add or modify them as your needs/application grows. - Just hit the web. Forget the
patch
entirely: just do the POST in your test and check the response. Of course, this might be slow, and will only work if you have internet. And you'll get goofy purists who will tell you to never to do this in a unit test. Maybe move it to an integration test if you run into one of those puristy people. But seriously, there's no substitute for doing what you're actually going to do in prod. The upside to doing this is that if the web service changes, then you'll know about it right away and can fix your code. Downside is it can slow down your test suite, and it's a potentially unreliable test (if the webservice is down, your test will fail...but it might actually be good to know that).
I recommend if the webservice is unstable (i.e liable to change), use option 2. Else, use option 1. Or do some combination of both (Mock
and patch
for a unit test, and hit the service on an integration test). Only you can decide!
HTH, good luck!
How can I mock (same request but different answers) requests with the response in Python?
You have several options:
- Rely on the order of the calls:
mocked_post.side_effect = ['a response', 'b response', 'c response']
- Write a bit of business logic in the mock
MOCKED_POST_RESPONSES = {
'[{"kind": "company"}]': 'a response',
'[{"kind": "people"}]': 'b response'
}
mocked_post.side_effect = lambda url, body, content_type: MOCKED_POST_RESPONSES[body]
- Or just use requests-mock module with a custom matcher https://requests-mock.readthedocs.io/en/latest/matching.html#custom-matching
Here is the decumentation on Mock.side_effect
: https://docs.python.org/3/library/unittest.mock.html#unittest.mock.Mock.side_effect
How to mock requests using pytest?
You can use requests-mock (PyPI), there is a fixture for a pytest usage.
For your example:
from correct.package import __BASE_URL
from requests import HTTPError
def test_get_employee(requests_mock):
test_id = 'random-id'
requests_mock.get(f'{__BASE_URL}/employee/{test_id}', json= {'name': 'awesome-mock'})
resp = get_employee('random-id')
assert resp == {'name': 'awesome-mock'}
def test_absent_employee(requests_mock):
test_id = 'does_not_exist'
requests_mock.get(f'{__BASE_URL}/employee/{test_id}', status_code=404)
with pytest.raises(HTTPError):
resp = get_employee(test_id)
How to mock a request in jasmine
Use spyOn(obj, methodName) function to install a spy onto request.get()
method, then use callFake(fn)
tell the spy to call a fake implementation when invoked. So that you can trigger the callback in the fake implementation.
index.js
:
import request from 'request';
export function getProducts() {
return new Promise((resolve, reject) => {
request.get({ url: 'http://ascott.com/products' }, (err, response, body) => {
if (err) return reject(err);
const result = JSON.parse(body);
if (result.value == 'yes') return resolve(1);
return resolve(0);
});
});
}
index.test.js
:
import { getProducts } from '.';
import request from 'request';
describe('69769551', () => {
it('should return 1', async () => {
spyOn(request, 'get').and.callFake((_, callback) => {
callback(null, null, JSON.stringify({ value: 'yes' }));
});
const actual = await getProducts();
expect(actual).toEqual(1);
});
it('should throw error', async () => {
const mError = new Error('network');
spyOn(request, 'get').and.callFake((_, callback) => {
callback(mError);
});
await expectAsync(getProducts()).toBeRejectedWithError('network');
});
});
test result:
Executing 2 defined specs...
Running in random order... (seed: 04537)
Test Suites & Specs:
1. 69769551
✔ should throw error (7ms)
✔ should return 1 (1ms)
>> Done!
Summary:
Passed
Suites: 1 of 1
Specs: 2 of 2
Expects: 2 (0 failures)
Finished in 0.017 seconds
package versions:
"jasmine": "^3.6.3"
"request": "^2.88.2"
mock response.elapsed in requests_mock
With example given above response.elapsed is bit more than 2 seconds. So I just used unittest.mock.patch to patch requests.request
mock_response = Mock(spec=requests.Response)
mock_response.elapsed = datetime.timedelta(seconds=2.0)
with patch("requests.request", return_value=mock_response):
my_code_here
Related Topics
How to Log While Using Multiprocessing in Python
In Python, How to Read the Exif Data for an Image
Prevent Sleep Mode Python (Wakelock on Python)
Basic Python Hello World Program Syntax Error
Priority of the Logical Operators Not, And, or in Python
In Matplotlib, What Does the Argument Mean in Fig.Add_Subplot(111)
Matplotlib: Format Axis Offset-Values to Whole Numbers or Specific Number
How to Make a Custom Activation Function with Only Python in Tensorflow
Why Does Using 'Arg=None' Fix Python's Mutable Default Argument Issue
Python Creating a Dictionary of Lists
What Can You Use Generator Functions For
How to Replace Text in a String Column of a Pandas Dataframe
How to Make a Call to an Executable from Python Script
Boto3 Client Noregionerror: You Must Specify a Region Error Only Sometimes