Permalink
Browse files

Mock urllib for testing GET requests

  • Loading branch information...
1 parent 22e49ee commit 7fe85b2a1d90d988c64836fe2a2733065271113f @ddgromit ddgromit committed Dec 30, 2011
Showing with 147 additions and 3 deletions.
  1. +1 −0 test/testdata/categories.json
  2. +143 −1 test/ticketevolution_test.py
  3. +3 −2 ticketevolution/ticketevolution.py
@@ -0,0 +1 @@
+{"per_page":3,"total_entries":87,"categories":[{"updated_at":"2011-04-15T05:39:23Z","parent":null,"url":"/categories/1","name":"Sports","id":"1"},{"updated_at":"2011-04-15T05:39:23Z","parent":{"url":"/categories/1","parent":null,"id":"1"},"url":"/categories/2","name":"Baseball","id":"2"},{"updated_at":"2011-04-15T05:39:23Z","parent":{"url":"/categories/2","parent":{"url":"/categories/1","parent":null,"id":"1"},"id":"2"},"url":"/categories/3","name":"MLB","id":"3"}],"current_page":1}
@@ -9,6 +9,7 @@
import unittest
import urllib
import sys
+import collections
sys.path.insert(0, os.path.join(os.path.dirname(__file__), '..'))
import ticketevolution
@@ -67,5 +68,146 @@ def testSignPost(self):
self.assertEqual(signature, "rcqPKvZzaC98SDOLBTTj8k4qIkmdDwnIMjYIKFw+aVY=")
+
+class ApiTest(unittest.TestCase):
+ def setUp(self):
+ self.mock_urllib = MockUrllib()
+ self.api = ticketevolution.Api(
+ client_token = "abcdefghijklmnopqrstuvwxyz123456",
+ client_secret = "abcdefghijklmnopqrstuvwxyz12345678901234",
+ sandbox = True,
+ alt_urllib = self.mock_urllib,
+ )
+
+ def testGetCall(self):
+ self.mock_urllib.AddFileHandler(
+ '/categories?per_page=3',
+ 'categories.json')
+
+ result = self.api.get('/categories',{
+ 'per_page':'3'
+ })
+ self.assertEqual(len(result['categories']),3)
+
+
+
+
+
+class MockUrllib(object):
+ '''A mock replacement for urllib2 that can return mock response
+ objects for requests against urls.'''
+
+ def __init__(self):
+ self._handlers = {}
+
+ def AddHandler(self, url, callback):
+ '''When urllib calls `url`, then use the string result of calling
+ `callback` as the response data'''
+ self._handlers[url] = callback
+
+ def AddFileHandler(self, url, filename):
+ '''When urllib calls `url`, use the contents of `filename` as the
+ data in the returned response object.
+
+ Args:
+ url:
+ path and query string vars, e.g. /categories?page_num=1
+ filename:
+ name of file in the 'testdata' directory
+ '''
+ # Get the full path of the test data file
+ directory = os.path.dirname(os.path.abspath(__file__))
+ test_data_dir = os.path.join(directory, 'testdata')
+ full_path = os.path.join(test_data_dir, filename)
+
+ def response_object_from_file():
+ return MockResponse(
+ resp_data = open(full_path).read(),
+ )
+
+ self.AddHandler(url,response_object_from_file)
+
+ def build_opener(self, *handlers):
+ return MockOpener(self._handlers)
+
+ def HTTPHandler(self, *args, **kwargs):
+ return None
+
+ def HTTPSHandler(self, *args, **kwargs):
+ return None
+
+ def OpenerDirector(self):
+ return self.build_opener()
+
+ def Request(self,url = "",data = None,headers = {}):
+ MockRequest = collections.namedtuple('MockRequest',['url','data','headers'])
+ req = MockRequest(url=url,data=data,headers=headers)
+ return req
+
+
+class MockOpener(object):
+ '''Simulates a urllib2 opener. Tests define handlers that return
+ mock response objects with predefined body data based on the URL.'''
+
+ def __init__(self, handlers):
+ '''
+ Arguments:
+ handlers:
+ A dict of URL paths w/ query strings (/categories?per_page=1) to
+ functions that, when called, will return mock response objects with
+ the appropriate data as the body.
+ '''
+ self._handlers = handlers
+ self._opened = False
+
+ def open(self, request):
+ url = request.url
+ data = request.data
+
+ # Only use the path and querystring as the key to the response map
+ path_and_querystring = url.split("//",1)[1]
+ path_and_querystring = "/" + path_and_querystring.split("/",1)[1]
+
+ if self._opened:
+ raise Exception('MockOpener already opened.')
+
+ if path_and_querystring in self._handlers:
+ self._opened = True
+ return self._handlers[path_and_querystring]()
+ else:
+ raise Exception('Unexpected URL %s (Checked: %s)' % (path_and_querystring, self._handlers))
+
+ def add_handler(self, *args, **kwargs):
+ pass
+
+ def close(self):
+ if not self._opened:
+ raise Exception('MockOpener closed before it was opened.')
+ self._opened = False
+
+class MockResponse(object):
+ '''Simulates parts of a urllib2 response object
+ '''
+ def __init__(self, resp_data):
+ self.resp_data = resp_data
+
+ # Taking headers directly from a cURL response
+ self.headers = {
+ 'Content-Type': 'application/vnd.ticketevolution.api+json; version=8; charset=utf-8',
+ 'Transfer-Encoding': 'chunked',
+ 'Connection': 'keep-alive',
+ 'Status': '200',
+ 'X-Powered-By': 'Phusion Passenger (mod_rails/mod_rack) 2.2.10',
+ 'ETag': '"efc7717e6d42fab9526e3a05f59ceb62"',
+ 'X-UA-Compatible': 'IE=Edge,chrome=1',
+ 'X-Runtime': '0.038400',
+ 'Cache-Control': 'max-age=0, private, must-revalidate',
+ 'Strict-Transport-Security': 'max-age=31536000',
+ 'Server': 'nginx/0.7.65 + Phusion Passenger 2.2.10 (mod_rails/mod_rack)',
+ }
+
+ def read(self):
+ return self.resp_data
+
if __name__ == '__main__':
- unittest.main()
+ unittest.main()
@@ -69,7 +69,8 @@ def __init__(self,
client_token,
client_secret,
sandbox=False,
- debug=False):
+ debug=False,
+ alt_urllib=None):
'''Instantiate a new ticketevolution.Api object.
Args:
@@ -89,7 +90,7 @@ def __init__(self,
self.client_token = client_token
self.client_secret = client_secret
- self._urllib = urllib2
+ self._urllib = alt_urllib or urllib2
self._input_encoding = None
self.API_VERSION = 8

0 comments on commit 7fe85b2

Please sign in to comment.