I have a view that perform a third party API call:
from .atoca_api_calls import atoca_api_call
def atoca_api_call_view(request):
# some code
data = atoca_api_call(**params) # the actual API call happens in this func
# some code
My intention is to mock just the atoca_api_call() func and not the whole view.
class AtocaTestCase(TestCase):
def setUp(self):
# some code
#patch('crm.atoca_api_calls.atoca_api_call')
def test_atoca_call(self, mock_atoca_api_call):
mock_atoca_api_call.return_value = MagicMock(
status_code=200,
response=some_json # just returns some json
)
url = reverse('crm:atoca-api-call') # url related to `atoca_api_call_view`
response = self.client.post(url, some_others_params)
# various asserts
The test works fine but atoca_api_call() is not mocked.
I'm aware of where to patch:
#patch('crm.views.atoca_api_call', autospec=True)
Raises a ValueError:
ValueError: Failed to insert expression "<MagicMock name='mock.__getitem__().__getitem__().__getitem__()().resolve_expression()' id='140048216397424'>" on crm.Company.atoca_id. F() expressions can only be used to update, not to insert.
It's probably a simple issue I don't catch for inexperience, any help is really appreciated.
As i see the atoca_api_call() it is mocked. The issue is the return value.
mock_atoca_api_call.return_value = MagicMock(
status_code=200,
response=some_json # just returns some json
)
This should ve a proper response i assume a JsonResponse or Response(data) not sure what are you returning. Try with:
mock_atoca_api_call.return_value = JsonResponse(
some_json # just returns some json
)
I have following function that I want to write unit test for
def get_user_data(user, session):
'''Given a github user, gets it data'''
url = f'https://api.github.com/users/{user}'
response = session.get(url)
json_response = response.json()
return json_response["login"]
This is how I am testing the above function
from unittest.mock import Mock, MagicMock
from requests import Session
response_payload = {"login": "agrawalo"}
fake_session = MagicMock(spec=Session)
fake_session.get.return_value.get_json.return_value = response_payload
If you notice the code above, instead of mocking "json" method on a response object I have created a mock for "get_json" method. This ends up creating get_json method on a response object. And now when I call
get_user_data("agrawalo", fake_session)
I get
<MagicMock name='mock.get().json().__getitem__()' id='139783470311800'>
Whereas I expect the above unittest to fail with NoAttributeError.
In python 3.8 there is a way to fix above problem using "seal" from unitest.mock.
# from python 3.8
from unittest.mock import seal
seal(fake_session)
get_user_data("agrawalo", fake_session)
How can I do this in python 2.7?
I'm writing some tests and I'd like to convert flask.Response objects to corresponding requests.Response objects. So I have 2 Flask apps (e.g. A and B), and A makes internal calls to B (via requests.post(url, json=payload)). My goal is to properly mock those calls without even launching any servers, and the current solution looks like the following:
from unittest import mock
...
def mock_B_request(url, json):
response = app_B.test_client().post(url, json=json) # flask.Response
# Some hacking should be done here,
# since flask.Respone doesn't have `.ok`, `.json()`, etc.,
# so it will break the code inside app_A
return response
...
# Inside the actual test method
with mock.patch('requests.post', side_effect=mock_B_request):
response = app_A.test_client().post(url, json=payload)
result = response.get_json()
...
Has someone already encountered with such a problem? What is the easiest solution here?
Eventually, I have found a very easy working solution:
def mock_B_request(url, **kwargs):
response = app_B.test_client().post(url, json=kwargs['json'])
# Hack a bit in order to make flask.Response
# support the same API as requests.Response
response_mock = mock.MagicMock()
response.status_code = response.status_code
response_mock.ok = response.status_code == 200
response_mock.json = lambda: response.get_json()
return response_mock
I hope it will be helpful for somebody.
I am writing an application that performs REST operations using Kenneth Reitz's requests library and I'm struggling to find a nice way to unit test these applications, because requests provides its methods via module-level methods.
What I want is the ability to synthesize the conversation between the two sides; provide a series of request assertions and responses.
It is in fact a little strange that the library has a blank page about end-user unit testing, while targeting user-friendliness and ease of use. There's however an easy-to-use library by Dropbox, unsurprisingly called responses. Here is its intro post. It says they've failed to employ httpretty, while stating no reason of the fail, and written a library with similar API.
import unittest
import requests
import responses
class TestCase(unittest.TestCase):
#responses.activate
def testExample(self):
responses.add(**{
'method' : responses.GET,
'url' : 'http://example.com/api/123',
'body' : '{"error": "reason"}',
'status' : 404,
'content_type' : 'application/json',
'adding_headers' : {'X-Foo': 'Bar'}
})
response = requests.get('http://example.com/api/123')
self.assertEqual({'error': 'reason'}, response.json())
self.assertEqual(404, response.status_code)
If you use specifically requests try httmock. It's wonderfully simple and elegant:
from httmock import urlmatch, HTTMock
import requests
# define matcher:
#urlmatch(netloc=r'(.*\.)?google\.com$')
def google_mock(url, request):
return 'Feeling lucky, punk?'
# open context to patch
with HTTMock(google_mock):
# call requests
r = requests.get('http://google.com/')
print r.content # 'Feeling lucky, punk?'
If you want something more generic (e.g. to mock any library making http calls) go for httpretty.
Almost as elegant:
import requests
import httpretty
#httpretty.activate
def test_one():
# define your patch:
httpretty.register_uri(httpretty.GET, "http://yipit.com/",
body="Find the best daily deals")
# use!
response = requests.get('http://yipit.com')
assert response.text == "Find the best daily deals"
HTTPretty is far more feature-rich - it offers also mocking status code, streaming responses, rotating responses, dynamic responses (with a callback).
You could use a mocking library such as Mocker to intercept the calls to the requests library and return specified results.
As a very simple example, consider this class which uses the requests library:
class MyReq(object):
def doSomething(self):
r = requests.get('https://api.github.com', auth=('user', 'pass'))
return r.headers['content-type']
Here's a unit test that intercepts the call to requests.get and returns a specified result for testing:
import unittest
import requests
import myreq
from mocker import Mocker, MockerTestCase
class MyReqTests(MockerTestCase):
def testSomething(self):
# Create a mock result for the requests.get call
result = self.mocker.mock()
result.headers
self.mocker.result({'content-type': 'mytest/pass'})
# Use mocker to intercept the call to requests.get
myget = self.mocker.replace("requests.get")
myget('https://api.github.com', auth=('user', 'pass'))
self.mocker.result(result)
self.mocker.replay()
# Now execute my code
r = myreq.MyReq()
v = r.doSomething()
# and verify the results
self.assertEqual(v, 'mytest/pass')
self.mocker.verify()
if __name__ == '__main__':
unittest.main()
When I run this unit test I get the following result:
.
----------------------------------------------------------------------
Ran 1 test in 0.004s
OK
Missing from these answers is requests-mock.
From their page:
>>> import requests
>>> import requests_mock
As a context manager:
>>> with requests_mock.mock() as m:
... m.get('http://test.com', text='data')
... requests.get('http://test.com').text
...
'data'
Or as a decorator:
>>> #requests_mock.mock()
... def test_func(m):
... m.get('http://test.com', text='data')
... return requests.get('http://test.com').text
...
>>> test_func()
'data'
using mocker like in srgerg's answer:
def replacer(method, endpoint, json_string):
from mocker import Mocker, ANY, CONTAINS
mocker = Mocker()
result = mocker.mock()
result.json()
mocker.count(1, None)
mocker.result(json_string)
replacement = mocker.replace("requests." + method)
replacement(CONTAINS(endpoint), params=ANY)
self.mocker.result(result)
self.mocker.replay()
For the requests library, this would intercept the request by method and endpoint you're hitting and replace the .json() on the response with the json_string passed in.
If you break out your response handler/parser into a separate function, you can work with requests.Response objects directly, without needing to mock the client-server interaction.
Code under test
from xml.dom import minidom
from requests.models import Response
def function_under_test(s3_response: Response):
doc = minidom.parseString(s3_response.text)
return (
s3_response.status_code,
doc.getElementsByTagName('Code').item(0).firstChild.data,
)
Test code
import unittest
from io import BytesIO
class Test(unittest.TestCase):
def test_it(self):
s3_response = Response()
s3_response.status_code = 404
s3_response.raw = BytesIO(b"""<?xml version="1.0" encoding="UTF-8"?>
<Error>
<Code>NoSuchKey</Code>
<Message>The resource you requested does not exist</Message>
<Resource>/mybucket/myfoto.jpg</Resource>
<RequestId>4442587FB7D0A2F9</RequestId>
</Error>
""")
parsed_response = function_under_test(s3_response)
self.assertEqual(404, parsed_response[0])
self.assertEqual("NoSuchKey", parsed_response[1])
There's a library for this, if you want to write your test server with Flask: requests-flask-adaptor
You just have to be careful with the order of imports when monkeypatching.
Is it possible to use webpy to serve JSON?
I built my website and I need to serve some information in JSON to interact with the Javascript on some pages.
I try to look for answers in the documentation, but I'm not able to find anything.
Thanks,
Giovanni
I wouldn't think you'd have to do any thing overly "special" for web.py to serve JSON.
import web
import json
class index:
def GET(self):
pyDict = {'one':1,'two':2}
web.header('Content-Type', 'application/json')
return json.dumps(pyDict)
It is certainly possible to serve JSON from webpy, But if you and choosing a framework, I would look at starlight and my fork twilight (for documentation).
It has a JSON wrapper for fixing the http headers for your json response.
it uses either the json or simplejson libraries for json handling the conversions to and from other objects.
I am using it right now and it is great.
https://bitbucket.org/marchon/twilight
in it you will find an example called ShowMeTheJson.py
that uses simple json
from starlight import *
from werkzeug.routing import Map
from werkzeug.routing import RuleFactory
import simplejson
class ShowMeTheResponses(App):
####################################################################
#
# Sample URLS to Test Responses
#
# http://localhost:8080/ root
#
# http://localhost:8080/json return JSON Mime Type Doc
#
###################################################################
#default
def hello(self):
return 'Hello, world!'
#dispatch('/')
def index(self):
return 'Hello Root!'
#dispatch('/html')
def indexhtml(self):
return HTML('Hello HTML')
#dispatch('/json')
def indexjson(self):
directions = {'N' : 'North', 'S' : 'South', 'E':'East', 'W' : 'West'}
return JSON(simplejson.dumps(directions))
if __name__ == '__main__':
from werkzeug import run_simple
run_simple('localhost', 8080, ShowMeTheResponses())