The implement of responses for mocking out the requests
Utility of responses
Basic
import responses
import requests
responses.add(responses.GET,'http://twitter.com/api/1/foobar',
json={'error': 'not found'}, status=404)
@responses.activate
def test_sample():
resp = requests.get('http://twitter.com/api/1/foobar')
assert resp.json() == {"error": "not found"}
assert resp.status_code = 404
... '''
Multiple responses
import responses
import requests
responses.add(responses.GET,'http://twitter.com/api/1/foobar',
status=500)
responses.add(responses.GET,'http://twitter.com/api/1/foobar',
body='{}', status=200,
content_type='application/json')
@responses.activate
def test_multiple():
resp = requests.get('http://twitter.com/api/1/foobar')
assert resp.status_code == 500
resp = requests.get('http://twitter.com/api/1/foobar')
assert resp.status_code == 200
... '''
Using a callback to modify the response
import responses
import requests
def response_callback(resp):
resp.callback_processed = True
return resp
with responses.RequestsMock(response_callback=response_callback) as m:
m.add(responses.GET, 'http://example.com', body=b'test')
resp = requests.get('http://example.com')
assert resp.text == "test"
assert hasattr(resp, 'callback_processed')
assert resp.callback_processed is True
... '''
Structure of responses
RequestMock
方法 | 作用 |
---|
_ init _ | 初始化 |
reset | 重置 |
add | 添加response |
add_passthru | |
remove | |
replace | |
add_callback | |
calls | |
_ enter _ | 启动mock |
_ exit _ | 恢复 |
activate | wrap testcase() |
_find_match | get response |
_on_request | return response |
start | 启动mock |
stop | 恢复 |
BaseResponse
方法 | 作用 |
---|
_ init _ | 初始化 |
_ eq _ | |
_ ne _ | |
_url_matches_strict | |
_url_matches | url -> response |
get_header | |
get_response | |
matches | |
Response
方法 | 作用 |
---|
_ init _ | 初始化 |
get_response | |
CallbackResponse
方法 | 作用 |
---|
_ init _ | 初始化 |
get_response | |
Module方法
方法 | 作用 |
---|
_is_string | |
_has_unicode | |
_clean_unicode | |
_is_redirect | |
get_wrapped | 包装testcase() |
_ensure_url_default_path | |
_handle_body | |
How it works
wrap test_example():
execute wrapped_test_example():
get_wrapped():
import inspect
import six
from functools import update_wrapper
_wrapper_template = """\
def wrapper%(signature)s:
with responses:
return func%(funcargs)s
"""
def get_wrapped(func, wrapper_template, evaldict):
if six.PY2:
args, a, kw, defaults = inspect.getargspec(func)
else:
args, a, kw, defaults, kwonlyargs, kwonlydefaults,
annotations = inspect.getfullargspec(func)
signature = inspect.formatargspec(args, a, kw, defaults)
is_bound_method = hasattr(func, '__self__')
if is_bound_method:
args = args[1:]
callargs = inspect.formatargspec(args, a, kw, None)
ctx = {'signature': signature, 'funcargs': callargs}
six.exec_(wrapper_template % ctx, evaldict)
wrapper = evaldict['wrapper']
update_wrapper(wrapper, func)
if is_bound_method:
wrapper = wrapper.__get__(func.__self__,
type(func.__self__))
return wrapper
... '''
RequestMock.start()
def start(self):
try:
from unittest import mock
except ImportError:
import mock
def unbound_on_send(adapter, request, *a, **kwargs):
return self._on_request(adapter, request, *a,
**kwargs)
self._patcher = mock.patch(
'requests.adapters.HTTPAdapter.send',
unbound_on_send)
self._patcher.start()
... '''
wrapped_test_example()
def wrapper%(args_signature)s:
with responses:
return test_example%(funcargs)s
... '''
延伸
- 支持更多HTTP请求框架 :urllib,httplib,aiohttp;
- 支持ORM :SQLAlchemy,peewee。