Kenneth Reitzのリクエストライブラリを使用してREST操作を実行するアプリケーションを作成していますが、リクエストはモジュールレベルのメソッドを介してメソッドを提供するため、これらのアプリケーションを単体テストするための優れた方法を見つけるのに苦労しています。
私が欲しいのは、両者の間の会話を統合する能力です。一連の要求アサーションと応答を提供します。
Kenneth Reitzのリクエストライブラリを使用してREST操作を実行するアプリケーションを作成していますが、リクエストはモジュールレベルのメソッドを介してメソッドを提供するため、これらのアプリケーションを単体テストするための優れた方法を見つけるのに苦労しています。
私が欲しいのは、両者の間の会話を統合する能力です。一連の要求アサーションと応答を提供します。
実際、ライブラリにエンドユーザーの単体テストに関する空白のページがあり、使いやすさと使いやすさを目標としているのは少し奇妙です。ただし、Dropboxには、当然のことながらと呼ばれる使いやすいライブラリがありresponses
ます。これがその紹介記事です。httpretty
失敗の理由はなく、採用に失敗したとのことで、同様のAPIを使用してライブラリを作成しました。
import unittest
import requests
import responses
class TestCase(unittest.TestCase):
@responses.activate
def testExample(self):
responses.add(**{
'method' : responses.GET,
'url' : 'http://example.com/api/123',
'body' : '{"error": "reason"}',
'status' : 404,
'content_type' : 'application/json',
'adding_headers' : {'X-Foo': 'Bar'}
})
response = requests.get('http://example.com/api/123')
self.assertEqual({'error': 'reason'}, response.json())
self.assertEqual(404, response.status_code)
特にリクエストを使用する場合は、httmockを試してください。それは素晴らしくシンプルでエレガントです:
from httmock import urlmatch, HTTMock
import requests
# define matcher:
@urlmatch(netloc=r'(.*\.)?google\.com$')
def google_mock(url, request):
return 'Feeling lucky, punk?'
# open context to patch
with HTTMock(google_mock):
# call requests
r = requests.get('http://google.com/')
print r.content # 'Feeling lucky, punk?'
より一般的なものが必要な場合(たとえば、http呼び出しを行うライブラリをモックする場合)は、httprettyにアクセスしてください。
ほぼ同じくらいエレガント:
import requests
import httpretty
@httpretty.activate
def test_one():
# define your patch:
httpretty.register_uri(httpretty.GET, "http://yipit.com/",
body="Find the best daily deals")
# use!
response = requests.get('http://yipit.com')
assert response.text == "Find the best daily deals"
HTTPrettyははるかに機能が豊富で、モックステータスコード、ストリーミングレスポンス、ローテーションレスポンス、ダイナミックレスポンス(コールバック付き)も提供します。
Mockerなどのモックライブラリを使用して、リクエストライブラリへの呼び出しをインターセプトし、指定された結果を返すことができます。
非常に簡単な例として、requestsライブラリを使用するこのクラスについて考えてみます。
class MyReq(object):
def doSomething(self):
r = requests.get('https://api.github.com', auth=('user', 'pass'))
return r.headers['content-type']
これは、への呼び出しをインターセプトし、テストのrequests.get
ために指定された結果を返す単体テストです。
import unittest
import requests
import myreq
from mocker import Mocker, MockerTestCase
class MyReqTests(MockerTestCase):
def testSomething(self):
# Create a mock result for the requests.get call
result = self.mocker.mock()
result.headers
self.mocker.result({'content-type': 'mytest/pass'})
# Use mocker to intercept the call to requests.get
myget = self.mocker.replace("requests.get")
myget('https://api.github.com', auth=('user', 'pass'))
self.mocker.result(result)
self.mocker.replay()
# Now execute my code
r = myreq.MyReq()
v = r.doSomething()
# and verify the results
self.assertEqual(v, 'mytest/pass')
self.mocker.verify()
if __name__ == '__main__':
unittest.main()
この単体テストを実行すると、次の結果が得られます。
.
----------------------------------------------------------------------
Ran 1 test in 0.004s
OK
これらの答えに欠けているのはrequests-mockです。
彼らのページから:
>>> import requests >>> import requests_mock
コンテキストマネージャーとして:
>>> with requests_mock.mock() as m: ... m.get('http://test.com', text='data') ... requests.get('http://test.com').text ... 'data'
またはデコレータとして:
>>> @requests_mock.mock() ... def test_func(m): ... m.get('http://test.com', text='data') ... return requests.get('http://test.com').text ... >>> test_func() 'data'
srgergの答えのようにモッカーを使用する:
def replacer(method, endpoint, json_string):
from mocker import Mocker, ANY, CONTAINS
mocker = Mocker()
result = mocker.mock()
result.json()
mocker.count(1, None)
mocker.result(json_string)
replacement = mocker.replace("requests." + method)
replacement(CONTAINS(endpoint), params=ANY)
self.mocker.result(result)
self.mocker.replay()
リクエストライブラリの場合、これにより、ヒットしているメソッドとエンドポイントによってリクエストがインターセプトされ、レスポンスの.json()が渡されたjson_stringに置き換えられます。
応答ハンドラー/パーサーを別の関数に分割するとrequests.Response objects
、クライアントとサーバーの相互作用を模倣することなく、直接操作できます。
テスト中のコード
from xml.dom import minidom
from requests.models import Response
def function_under_test(s3_response: Response):
doc = minidom.parseString(s3_response.text)
return (
s3_response.status_code,
doc.getElementsByTagName('Code').item(0).firstChild.data,
)
テストコード
import unittest
from io import BytesIO
class Test(unittest.TestCase):
def test_it(self):
s3_response = Response()
s3_response.status_code = 404
s3_response.raw = BytesIO(b"""<?xml version="1.0" encoding="UTF-8"?>
<Error>
<Code>NoSuchKey</Code>
<Message>The resource you requested does not exist</Message>
<Resource>/mybucket/myfoto.jpg</Resource>
<RequestId>4442587FB7D0A2F9</RequestId>
</Error>
""")
parsed_response = function_under_test(s3_response)
self.assertEqual(404, parsed_response[0])
self.assertEqual("NoSuchKey", parsed_response[1])