Github: https://github.com/getsentry/responses
反応
Pythonライブラリのリクエストを模擬するためのユーティリティライブラリ。
注意
レスポンスにはPython 2.7以降が必要で、> 2.0以上のリクエスト
インストール
pip install responses
基本
responses
のコアは模擬応答の登録から来ます:
import responses
import requests
@responses.activate
def test_simple():
responses.add(responses.GET, 'http://twitter.com/api/1/foobar',
json={'error': 'not found'}, status=404)
resp = requests.get('http://twitter.com/api/1/foobar')
assert resp.json() == {"error": "not found"}
assert len(responses.calls) == 1
assert responses.calls[0].request.url == 'http://twitter.com/api/1/foobar'
assert responses.calls[0].response.text == '{"error": "not found"}'
一致しなかったURLを取得しようとすると、 responses
はConnectionError
を送出しConnectionError
:
import responses
import requests
from requests.exceptions import ConnectionError
@responses.activate
def test_simple():
with pytest.raises(ConnectionError):
requests.get('http://twitter.com/api/1/foobar')
最後に、 Exception
を本文として渡してリクエストのエラーをトリガーすることができます。
import responses
import requests
@responses.activate
def test_simple():
responses.add(responses.GET, 'http://twitter.com/api/1/foobar',
body=Exception('...'))
with pytest.raises(Exception):
requests.get('http://twitter.com/api/1/foobar')
応答パラメータ
レスポンスはadd
paramsを介して自動的に登録されますが、直接渡すこともできます:
import responses
responses.add(
responses.Response(
method='GET',
url='http://example.com',
),
)
次の属性は、応答モックに渡すことができます。
- メソッド(
str
) - HTTPメソッド(GET、POSTなど)。
- url(
str
またはコンパイルされた正規表現) - 完全なリソースのURL。
- match_querystring(
bool
) - デフォルトでは無効です。 要求を一致させる場合は、クエリ文字列を含めます。
- body(
str
またはBufferedReader
) - レスポンスボディ。
- ジョソン
- JSON応答本体を表すPythonオブジェクト。 適切なContent-Typeを自動的に設定します。
- status(
int
) - HTTPステータスコード。
- content_type(
content_type
) - デフォルトは
text/plain
です。 - ヘッダー(
dict
) - 応答ヘッダー。
- ストリーム(
bool
) - デフォルトでは無効です。 応答でストリーミングAPIを使用する必要があることを示します。
動的応答
コールバックを利用して動的な応答を提供することができます。 コールバックは、( status
、 headers
、 body
)のタプルを返す必要があります。
import json
import responses
import requests
@responses.activate
def test_calc_api():
def request_callback(request):
payload = json.loads(request.body)
resp_body = {'value': sum(payload['numbers'])}
headers = {'request-id': '728d329e-0e86-11e4-a748-0c84dc037c13'}
return (200, headers, json.dumps(resp_body))
responses.add_callback(
responses.POST, 'http://calc.com/sum',
callback=request_callback,
content_type='application/json',
)
resp = requests.post(
'http://calc.com/sum',
json.dumps({'numbers': [1, 2, 3]}),
headers={'content-type': 'application/json'},
)
assert resp.json() == {'value': 6}
assert len(responses.calls) == 1
assert responses.calls[0].request.url == 'http://calc.com/sum'
assert responses.calls[0].response.text == '{"value": 6}'
assert (
responses.calls[0].response.headers['request-id'] ==
'728d329e-0e86-11e4-a748-0c84dc037c13'
)
コンテキストマネージャとしての応答
import responses
import requests
def test_my_api():
with responses.RequestsMock() as rsps:
rsps.add(responses.GET, 'http://twitter.com/api/1/foobar',
body='{}', status=200,
content_type='application/json')
resp = requests.get('http://twitter.com/api/1/foobar')
assert resp.status_code == 200
# outside the context manager requests will hit the remote server
resp = requests.get('http://twitter.com/api/1/foobar')
resp.status_code == 404
宣言された応答に関するアサーション
コンテキストマネージャとして使用する場合、URLは登録されているがアクセスされていない場合、レスポンスはデフォルトでアサーションエラーを発生させます。 これは、 assert_all_requests_are_fired
値を渡すことで無効にすることができます:
import responses
import requests
def test_my_api():
with responses.RequestsMock(assert_all_requests_are_fired=False) as rsps:
rsps.add(responses.GET, 'http://twitter.com/api/1/foobar',
body='{}', status=200,
content_type='application/json')
複数の回答
同じURLに対して複数の応答を追加することもできます。
import responses
import requests
@responses.activate
def test_my_api():
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')
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
コールバックを使用してレスポンスを変更する
サブクラス化/ mixinsによるリクエストでカスタマイズされた処理を使用する場合、または低レベルでリクエストとやりとりするライブラリツールがある場合は、テストの環境を完全にシミュレートするために拡張レスポンスオブジェクトを拡張レスポンスオブジェクトに追加する必要があります。 response_callbackを使用することができます。これは、呼び出し側に返される前にライブラリによってラップされます。 コールバックは、単一の引数として応答を受け取り、単一の応答オブジェクトを返すことが期待されます。
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
実際のリクエストを通す
場合によっては、特定のリクエストがレスポンスを通過し、実際のサーバーにヒットすることを許可したい場合があります。 これは ‘パススルー’の方法で行うことができます:
import responses
@responses.activate
def test_my_api():
responses.add_passthru('https://percy.io')
これにより、その接頭辞に一致する要求(モック応答として登録されていない)が、標準的な動作を使用してパススルーに許可されます。