Merge "Allow doing real_http per mock via the mocker"
This commit is contained in:
commit
39f1f74b6b
@ -132,3 +132,18 @@ The Mocker object takes the following parameters:
|
|||||||
...
|
...
|
||||||
'resp'
|
'resp'
|
||||||
200
|
200
|
||||||
|
|
||||||
|
*New in 1.1*
|
||||||
|
|
||||||
|
Similarly when using a mocker you can register an individual URI to bypass the mocking infrastructure and make a real request. Note this only works when using the mocker and not when directly mounting an adapter.
|
||||||
|
|
||||||
|
.. doctest::
|
||||||
|
|
||||||
|
>>> with requests_mock.Mocker() as m:
|
||||||
|
... m.register_uri('GET', 'http://test.com', text='resp')
|
||||||
|
... m.register_uri('GET', 'http://www.google.com', real_http=True)
|
||||||
|
... print(requests.get('http://test.com').text)
|
||||||
|
... print(requests.get('http://www.google.com').status_code) # doctest: +SKIP
|
||||||
|
...
|
||||||
|
'resp'
|
||||||
|
200
|
||||||
|
@ -151,10 +151,21 @@ class _RequestHistoryTracker(object):
|
|||||||
return len(self.request_history)
|
return len(self.request_history)
|
||||||
|
|
||||||
|
|
||||||
|
class _RunRealHTTP(Exception):
|
||||||
|
"""A fake exception to jump out of mocking and allow a real request.
|
||||||
|
|
||||||
|
This exception is caught at the mocker level and allows it to execute this
|
||||||
|
request through the real requests mechanism rather than the mocker.
|
||||||
|
|
||||||
|
It should never be exposed to a user.
|
||||||
|
"""
|
||||||
|
|
||||||
|
|
||||||
class _Matcher(_RequestHistoryTracker):
|
class _Matcher(_RequestHistoryTracker):
|
||||||
"""Contains all the information about a provided URL to match."""
|
"""Contains all the information about a provided URL to match."""
|
||||||
|
|
||||||
def __init__(self, method, url, responses, complete_qs, request_headers):
|
def __init__(self, method, url, responses, complete_qs, request_headers,
|
||||||
|
real_http):
|
||||||
"""
|
"""
|
||||||
:param bool complete_qs: Match the entire query string. By default URLs
|
:param bool complete_qs: Match the entire query string. By default URLs
|
||||||
match if all the provided matcher query arguments are matched and
|
match if all the provided matcher query arguments are matched and
|
||||||
@ -162,6 +173,7 @@ class _Matcher(_RequestHistoryTracker):
|
|||||||
require that the entire query string needs to match.
|
require that the entire query string needs to match.
|
||||||
"""
|
"""
|
||||||
super(_Matcher, self).__init__()
|
super(_Matcher, self).__init__()
|
||||||
|
|
||||||
self._method = method
|
self._method = method
|
||||||
self._url = url
|
self._url = url
|
||||||
try:
|
try:
|
||||||
@ -171,6 +183,7 @@ class _Matcher(_RequestHistoryTracker):
|
|||||||
self._responses = responses
|
self._responses = responses
|
||||||
self._complete_qs = complete_qs
|
self._complete_qs = complete_qs
|
||||||
self._request_headers = request_headers
|
self._request_headers = request_headers
|
||||||
|
self._real_http = real_http
|
||||||
|
|
||||||
def _match_method(self, request):
|
def _match_method(self, request):
|
||||||
if self._method is ANY:
|
if self._method is ANY:
|
||||||
@ -248,6 +261,11 @@ class _Matcher(_RequestHistoryTracker):
|
|||||||
if not self._match(request):
|
if not self._match(request):
|
||||||
return None
|
return None
|
||||||
|
|
||||||
|
# doing this before _add_to_history means real requests are not stored
|
||||||
|
# in the request history. I'm not sure what is better here.
|
||||||
|
if self._real_http:
|
||||||
|
raise _RunRealHTTP()
|
||||||
|
|
||||||
if len(self._responses) > 1:
|
if len(self._responses) > 1:
|
||||||
response_matcher = self._responses.pop(0)
|
response_matcher = self._responses.pop(0)
|
||||||
else:
|
else:
|
||||||
@ -294,19 +312,24 @@ class Adapter(BaseAdapter, _RequestHistoryTracker):
|
|||||||
"""
|
"""
|
||||||
complete_qs = kwargs.pop('complete_qs', False)
|
complete_qs = kwargs.pop('complete_qs', False)
|
||||||
request_headers = kwargs.pop('request_headers', {})
|
request_headers = kwargs.pop('request_headers', {})
|
||||||
|
real_http = kwargs.pop('_real_http', False)
|
||||||
|
|
||||||
if response_list and kwargs:
|
if response_list and kwargs:
|
||||||
raise RuntimeError('You should specify either a list of '
|
raise RuntimeError('You should specify either a list of '
|
||||||
'responses OR response kwargs. Not both.')
|
'responses OR response kwargs. Not both.')
|
||||||
|
elif real_http and (response_list or kwargs):
|
||||||
|
raise RuntimeError('You should specify either response data '
|
||||||
|
'OR real_http. Not both.')
|
||||||
elif not response_list:
|
elif not response_list:
|
||||||
response_list = [kwargs]
|
response_list = [] if real_http else [kwargs]
|
||||||
|
|
||||||
responses = [response._MatcherResponse(**k) for k in response_list]
|
responses = [response._MatcherResponse(**k) for k in response_list]
|
||||||
matcher = _Matcher(method,
|
matcher = _Matcher(method,
|
||||||
url,
|
url,
|
||||||
responses,
|
responses,
|
||||||
complete_qs=complete_qs,
|
complete_qs=complete_qs,
|
||||||
request_headers=request_headers)
|
request_headers=request_headers,
|
||||||
|
real_http=real_http)
|
||||||
self.add_matcher(matcher)
|
self.add_matcher(matcher)
|
||||||
return matcher
|
return matcher
|
||||||
|
|
||||||
|
@ -34,7 +34,6 @@ class MockerCore(object):
|
|||||||
"""
|
"""
|
||||||
|
|
||||||
_PROXY_FUNCS = set(['last_request',
|
_PROXY_FUNCS = set(['last_request',
|
||||||
'register_uri',
|
|
||||||
'add_matcher',
|
'add_matcher',
|
||||||
'request_history',
|
'request_history',
|
||||||
'called',
|
'called',
|
||||||
@ -70,6 +69,10 @@ class MockerCore(object):
|
|||||||
except exceptions.NoMockAddress:
|
except exceptions.NoMockAddress:
|
||||||
if not self._real_http:
|
if not self._real_http:
|
||||||
raise
|
raise
|
||||||
|
except adapter._RunRealHTTP:
|
||||||
|
# this mocker wants you to run the request through the real
|
||||||
|
# requests library rather than the mocking. Let it.
|
||||||
|
pass
|
||||||
finally:
|
finally:
|
||||||
requests.Session.get_adapter = real_get_adapter
|
requests.Session.get_adapter = real_get_adapter
|
||||||
|
|
||||||
@ -95,6 +98,12 @@ class MockerCore(object):
|
|||||||
|
|
||||||
raise AttributeError(name)
|
raise AttributeError(name)
|
||||||
|
|
||||||
|
def register_uri(self, *args, **kwargs):
|
||||||
|
# you can pass real_http here, but it's private to pass direct to the
|
||||||
|
# adapter, because if you pass direct to the adapter you'll see the exc
|
||||||
|
kwargs['_real_http'] = kwargs.pop('real_http', False)
|
||||||
|
return self._adapter.register_uri(*args, **kwargs)
|
||||||
|
|
||||||
def request(self, *args, **kwargs):
|
def request(self, *args, **kwargs):
|
||||||
return self.register_uri(*args, **kwargs)
|
return self.register_uri(*args, **kwargs)
|
||||||
|
|
||||||
|
@ -27,12 +27,14 @@ class TestMatcher(base.TestCase):
|
|||||||
request_method='GET',
|
request_method='GET',
|
||||||
complete_qs=False,
|
complete_qs=False,
|
||||||
headers=None,
|
headers=None,
|
||||||
request_headers={}):
|
request_headers={},
|
||||||
|
real_http=False):
|
||||||
matcher = adapter._Matcher(matcher_method,
|
matcher = adapter._Matcher(matcher_method,
|
||||||
target,
|
target,
|
||||||
[],
|
[],
|
||||||
complete_qs,
|
complete_qs,
|
||||||
request_headers)
|
request_headers,
|
||||||
|
real_http=real_http)
|
||||||
request = adapter._RequestObjectProxy._create(request_method,
|
request = adapter._RequestObjectProxy._create(request_method,
|
||||||
url,
|
url,
|
||||||
headers)
|
headers)
|
||||||
|
@ -15,6 +15,7 @@ import requests
|
|||||||
|
|
||||||
import requests_mock
|
import requests_mock
|
||||||
from requests_mock import compat
|
from requests_mock import compat
|
||||||
|
from requests_mock import exceptions
|
||||||
from requests_mock.tests import base
|
from requests_mock.tests import base
|
||||||
|
|
||||||
original_send = requests.Session.send
|
original_send = requests.Session.send
|
||||||
@ -243,3 +244,38 @@ class MockerHttpMethodsTests(base.TestCase):
|
|||||||
mock_obj = m.delete(self.URL, text=self.TEXT)
|
mock_obj = m.delete(self.URL, text=self.TEXT)
|
||||||
self.assertResponse(requests.delete(self.URL))
|
self.assertResponse(requests.delete(self.URL))
|
||||||
self.assertTrue(mock_obj.called)
|
self.assertTrue(mock_obj.called)
|
||||||
|
|
||||||
|
@requests_mock.Mocker()
|
||||||
|
def test_mocker_real_http_and_responses(self, m):
|
||||||
|
self.assertRaises(RuntimeError,
|
||||||
|
m.get,
|
||||||
|
self.URL,
|
||||||
|
text='abcd',
|
||||||
|
real_http=True)
|
||||||
|
|
||||||
|
@requests_mock.Mocker()
|
||||||
|
def test_mocker_real_http(self, m):
|
||||||
|
data = 'testdata'
|
||||||
|
|
||||||
|
uri1 = 'fake://example.com/foo'
|
||||||
|
uri2 = 'fake://example.com/bar'
|
||||||
|
uri3 = 'fake://example.com/baz'
|
||||||
|
|
||||||
|
m.get(uri1, text=data)
|
||||||
|
m.get(uri2, real_http=True)
|
||||||
|
|
||||||
|
self.assertEqual(data, requests.get(uri1).text)
|
||||||
|
|
||||||
|
# This should fail because requests can't get an adapter for mock://
|
||||||
|
# but it shows that it has tried and would have made a request.
|
||||||
|
self.assertRaises(requests.exceptions.InvalidSchema,
|
||||||
|
requests.get,
|
||||||
|
uri2)
|
||||||
|
|
||||||
|
# This fails because real_http is not set on the mocker
|
||||||
|
self.assertRaises(exceptions.NoMockAddress,
|
||||||
|
requests.get,
|
||||||
|
uri3)
|
||||||
|
|
||||||
|
# do it again to make sure the mock is still in place
|
||||||
|
self.assertEqual(data, requests.get(uri1).text)
|
||||||
|
Loading…
x
Reference in New Issue
Block a user