Skip to content

Instantly share code, notes, and snippets.

@lastorset
Forked from kesor/gist:1179782
Last active October 21, 2023 22:55
Show Gist options
  • Save lastorset/075a8d4db8937509c33f4a36c6e3de7f to your computer and use it in GitHub Desktop.
Save lastorset/075a8d4db8937509c33f4a36c6e3de7f to your computer and use it in GitHub Desktop.
Google AppEngine URLFetch in Unit Tests
from google.appengine.api import apiproxy_stub, apiproxy_stub_map, urlfetch
from google.appengine.ext import testbed
import unittest
class FetchServiceMock(apiproxy_stub.APIProxyStub):
"""
Example::
with FetchServiceMock(content='A MOCKERY', status_code=200) as fetch_mock:
ret = main.method('amok')
self.assertEquals('http://amok.com', fetch_mock.request.url())
self.assertEquals('A MOCKERY', ret)
Inspired by https://gist.github.com/kesor/1179782
"""
def __init__(
self, content='', status_code=500, headers=None,
final_url=None, content_was_truncated=False):
"""
:param final_url: URL after redirects.
"""
# type: (basestring, int, Optional[Mapping], Optional[basestring], bool) -> None
super(FetchServiceMock, self).__init__(testbed.URLFETCH_SERVICE_NAME)
self.return_values = {
'content': content,
'statuscode': status_code,
'headers': headers if headers else {},
'finalurl': final_url,
'contentwastruncated': content_was_truncated,
}
def __enter__(self):
apiproxy_stub_map.apiproxy.ReplaceStub(
testbed.URLFETCH_SERVICE_NAME, self)
return self
def __exit__(self, exc_type, exc_val, exc_tb):
complaining_mock = FetchServiceMock(
content='Error: {} was reset. You must either mock urlfetch again '
'or run Testbed.init_urlfetch_stub'.format(FetchServiceMock.__name__),
status_code=418)
apiproxy_stub_map.apiproxy.ReplaceStub(
testbed.URLFETCH_SERVICE_NAME, complaining_mock)
def _Dynamic_Fetch(self, request, response):
rv = self.return_values
response.set_content(rv['content'])
response.set_statuscode(rv['statuscode'])
for header_key, header_value in rv['headers'].iteritems():
new_header = response.add_header() # prototype for a header
new_header.set_key(header_key)
new_header.set_value(header_value)
response.set_finalurl(rv['finalurl'] or request.url)
response.set_contentwastruncated(rv['contentwastruncated'])
# allow to query the object after it is used
self.request = request
self.response = response
class TestFetchServiceMock(unittest.TestCase):
def setUp(self):
self.testbed = testbed.Testbed()
self.testbed.activate()
self.testbed.init_urlfetch_stub()
def test_mocking(self):
with FetchServiceMock(self.testbed, 'A MOCKERY') as fetch_mock:
fetched = urlfetch.fetch('http://httpstat.us/200')
self.assertEqual(fetched.content, 'A MOCKERY')
self.assertEqual(fetched.status_code, 500)
self.assertEqual('http://httpstat.us/200', fetch_mock.request.url())
def test_mock_resets(self):
with FetchServiceMock(self.testbed, 'A MOCKERY'):
active_stub = apiproxy_stub_map.apiproxy.GetStub(
testbed.URLFETCH_SERVICE_NAME)
self.assertIsInstance(active_stub, FetchServiceMock)
active_stub = apiproxy_stub_map.apiproxy.GetStub(
testbed.URLFETCH_SERVICE_NAME)
self.assertNotIsInstance(active_stub, FetchServiceMock)
# Enable to actually call the Internet
if False:
fetched2 = urlfetch.fetch('http://httpstat.us/200')
self.assertEqual(fetched2.content, '200 OK')
self.assertEqual(fetched2.status_code, 200)
@caxton
Copy link

caxton commented Dec 6, 2018

Is it possible to set content type like 'application/json'?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment