Replace mox in tests with requests-mock

requests-mock is a tool specifically designed for mocking responses from
the requests library. Use that instead of handling mox and mock
ourselves.

Change-Id: Ifd855b8d6c1b401e29ac210593c48d2da87a571b
This commit is contained in:
Jamie Lennox 2014-12-16 15:36:40 +10:00
parent b3d2c636ec
commit 02b1a05226
2 changed files with 57 additions and 125 deletions

@ -5,9 +5,10 @@ hacking>=0.8.0,<0.9
coverage>=3.6 coverage>=3.6
discover discover
mox3>=0.7.0
mock>=1.0 mock>=1.0
oslosphinx>=2.2.0 # Apache-2.0 oslosphinx>=2.2.0 # Apache-2.0
sphinx>=1.1.2,!=1.2.0,!=1.3b1,<1.3 sphinx>=1.1.2,!=1.2.0,!=1.3b1,<1.3
testrepository>=0.0.18 testrepository>=0.0.18
testtools>=0.9.36,!=1.2.0 testtools>=0.9.36,!=1.2.0
fixtures>=0.3.14
requests-mock>=0.6.0 # Apache-2.0

@ -14,9 +14,8 @@
# under the License. # under the License.
import json import json
import mock
from mox3 import mox
import requests import requests
from requests_mock.contrib import fixture
import six import six
from six.moves.urllib import parse from six.moves.urllib import parse
import testtools import testtools
@ -33,16 +32,11 @@ class TestClient(testtools.TestCase):
def setUp(self): def setUp(self):
super(TestClient, self).setUp() super(TestClient, self).setUp()
self.mock = mox.Mox() self.mock = self.useFixture(fixture.Fixture())
self.mock.StubOutWithMock(requests.Session, 'request')
self.endpoint = 'http://example.com:9292' self.endpoint = 'http://example.com:9292'
self.client = http.HTTPClient(self.endpoint, token=u'abc123') self.client = http.HTTPClient(self.endpoint, token=u'abc123')
def tearDown(self):
super(TestClient, self).tearDown()
self.mock.UnsetStubs()
def test_identity_headers_and_token(self): def test_identity_headers_and_token(self):
identity_headers = { identity_headers = {
'X-Auth-Token': 'auth_token', 'X-Auth-Token': 'auth_token',
@ -97,25 +91,22 @@ class TestClient(testtools.TestCase):
# when creating the http client, the session headers don't contain # when creating the http client, the session headers don't contain
# the X-Auth-Token key. # the X-Auth-Token key.
identity_headers = { identity_headers = {
b'X-User-Id': b'user', 'X-User-Id': b'user',
b'X-Tenant-Id': b'tenant', 'X-Tenant-Id': b'tenant',
b'X-Roles': b'roles', 'X-Roles': b'roles',
b'X-Identity-Status': b'Confirmed', 'X-Identity-Status': b'Confirmed',
b'X-Service-Catalog': b'service_catalog', 'X-Service-Catalog': b'service_catalog',
} }
kwargs = {'identity_headers': identity_headers} kwargs = {'identity_headers': identity_headers}
http_client = http.HTTPClient(self.endpoint, **kwargs) http_client = http.HTTPClient(self.endpoint, **kwargs)
def check_headers(*args, **kwargs): path = '/v1/images/my-image'
headers = kwargs.get('headers') self.mock.get(self.endpoint + path)
for k, v in six.iteritems(identity_headers): http_client.get(path)
self.assertEqual(v, headers[k])
return utils.FakeResponse({}, six.StringIO('{}')) headers = self.mock.last_request.headers
for k, v in six.iteritems(identity_headers):
with mock.patch.object(http_client.session, 'request') as mreq: self.assertEqual(v, headers[k])
mreq.side_effect = check_headers
http_client.get('http://example.com:9292/v1/images/my-image')
def test_connection_refused(self): def test_connection_refused(self):
""" """
@ -123,43 +114,27 @@ class TestClient(testtools.TestCase):
And the error should list the host and port that refused the And the error should list the host and port that refused the
connection connection
""" """
requests.Session.request( def cb(request, context):
mox.IgnoreArg(), raise requests.exceptions.ConnectionError()
mox.IgnoreArg(),
data=mox.IgnoreArg(), path = '/v1/images/detail?limit=20'
headers=mox.IgnoreArg(), self.mock.get(self.endpoint + path, text=cb)
stream=mox.IgnoreArg(),
).AndRaise(requests.exceptions.ConnectionError()) comm_err = self.assertRaises(glanceclient.exc.CommunicationError,
self.mock.ReplayAll() self.client.get,
try: '/v1/images/detail?limit=20')
self.client.get('/v1/images/detail?limit=20')
#NOTE(alaski) We expect exc.CommunicationError to be raised self.assertIn(self.endpoint, comm_err.message)
# so we should never reach this point. try/except is used here
# rather than assertRaises() so that we can check the body of
# the exception.
self.fail('An exception should have bypassed this line.')
except glanceclient.exc.CommunicationError as comm_err:
fail_msg = ("Exception message '%s' should contain '%s'" %
(comm_err.message, self.endpoint))
self.assertTrue(self.endpoint in comm_err.message, fail_msg)
def test_http_encoding(self): def test_http_encoding(self):
# Lets fake the response path = '/v1/images/detail'
# returned by requests text = 'Ok'
response = 'Ok' self.mock.get(self.endpoint + path, text=text,
headers = {"Content-Type": "text/plain"} headers={"Content-Type": "text/plain"})
fake = utils.FakeResponse(headers, six.StringIO(response))
requests.Session.request(
mox.IgnoreArg(),
mox.IgnoreArg(),
data=mox.IgnoreArg(),
stream=mox.IgnoreArg(),
headers=mox.IgnoreArg()).AndReturn(fake)
self.mock.ReplayAll()
headers = {"test": u'ni\xf1o'} headers = {"test": u'ni\xf1o'}
resp, body = self.client.get('/v1/images/detail', headers=headers) resp, body = self.client.get(path, headers=headers)
self.assertEqual(fake, resp) self.assertEqual(text, resp.text)
def test_headers_encoding(self): def test_headers_encoding(self):
value = u'ni\xf1o' value = u'ni\xf1o'
@ -171,18 +146,14 @@ class TestClient(testtools.TestCase):
def test_raw_request(self): def test_raw_request(self):
" Verify the path being used for HTTP requests reflects accurately. " " Verify the path being used for HTTP requests reflects accurately. "
headers = {"Content-Type": "text/plain"} headers = {"Content-Type": "text/plain"}
response = 'Ok' text = 'Ok'
fake = utils.FakeResponse({}, six.StringIO(response)) path = '/v1/images/detail'
requests.Session.request(
mox.IgnoreArg(), self.mock.get(self.endpoint + path, text=text, headers=headers)
mox.IgnoreArg(),
data=mox.IgnoreArg(),
stream=mox.IgnoreArg(),
headers=mox.IgnoreArg()).AndReturn(fake)
self.mock.ReplayAll()
resp, body = self.client.get('/v1/images/detail', headers=headers) resp, body = self.client.get('/v1/images/detail', headers=headers)
self.assertEqual(fake, resp) self.assertEqual(headers, resp.headers)
self.assertEqual(text, resp.text)
def test_parse_endpoint(self): def test_parse_endpoint(self):
endpoint = 'http://example.com:9292' endpoint = 'http://example.com:9292'
@ -199,76 +170,36 @@ class TestClient(testtools.TestCase):
self.assertEqual(test_client.timeout, 600.0) self.assertEqual(test_client.timeout, 600.0)
def test_http_chunked_request(self): def test_http_chunked_request(self):
# Lets fake the response text = "Ok"
# returned by requests data = six.StringIO(text)
response = "Ok" path = '/v1/images/'
data = six.StringIO(response) self.mock.post(self.endpoint + path, text=text)
fake = utils.FakeResponse({}, data)
requests.Session.request(
mox.IgnoreArg(),
mox.IgnoreArg(),
stream=mox.IgnoreArg(),
data=mox.IsA(types.GeneratorType),
headers=mox.IgnoreArg()).AndReturn(fake)
self.mock.ReplayAll()
headers = {"test": u'chunked_request'} headers = {"test": u'chunked_request'}
resp, body = self.client.post('/v1/images/', resp, body = self.client.post(path, headers=headers, data=data)
headers=headers, data=data) self.assertIsInstance(self.mock.last_request.body, types.GeneratorType)
self.assertEqual(fake, resp) self.assertEqual(text, resp.text)
def test_http_json(self): def test_http_json(self):
data = {"test": "json_request"} data = {"test": "json_request"}
fake = utils.FakeResponse({}, b"OK") path = '/v1/images'
text = 'OK'
def test_json(passed_data): self.mock.post(self.endpoint + path, text=text)
"""
This function tests whether the data
being passed to request's method is
a valid json or not.
This function will be called by pymox
:params passed_data: The data being
passed to requests.Session.request.
"""
if not isinstance(passed_data, six.string_types):
return False
try:
passed_data = json.loads(passed_data)
return data == passed_data
except (TypeError, ValueError):
return False
requests.Session.request(
mox.IgnoreArg(),
mox.IgnoreArg(),
stream=mox.IgnoreArg(),
data=mox.Func(test_json),
headers=mox.IgnoreArg()).AndReturn(fake)
self.mock.ReplayAll()
headers = {"test": u'chunked_request'} headers = {"test": u'chunked_request'}
resp, body = self.client.post('/v1/images/', resp, body = self.client.post(path, headers=headers, data=data)
headers=headers,
data=data) self.assertEqual(text, resp.text)
self.assertEqual(fake, resp) self.assertIsInstance(self.mock.last_request.body, six.string_types)
self.assertEqual(data, json.loads(self.mock.last_request.body))
def test_http_chunked_response(self): def test_http_chunked_response(self):
headers = {"Content-Type": "application/octet-stream"}
data = "TEST" data = "TEST"
fake = utils.FakeResponse(headers, six.StringIO(data)) path = '/v1/images/'
self.mock.get(self.endpoint + path, body=six.StringIO(data),
headers={"Content-Type": "application/octet-stream"})
requests.Session.request( resp, body = self.client.get(path)
mox.IgnoreArg(),
mox.IgnoreArg(),
stream=mox.IgnoreArg(),
data=mox.IgnoreArg(),
headers=mox.IgnoreArg()).AndReturn(fake)
self.mock.ReplayAll()
headers = {"test": u'chunked_request'}
resp, body = self.client.get('/v1/images/')
self.assertTrue(isinstance(body, types.GeneratorType)) self.assertTrue(isinstance(body, types.GeneratorType))
self.assertEqual([data], list(body)) self.assertEqual([data], list(body))