How to Mock Requests and the Response

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:

  1. 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 to patch over requests.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.
  2. 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:

  1. Rely on the order of the calls:
mocked_post.side_effect = ['a response', 'b response', 'c response']

  1. 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]

  1. 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



Leave a reply



Submit