Responses
Responses - A utility for mocking out the Python Requests library,主要用于Web Service mocking。
安裝
使用pip3 install responses
基本使用
responses
的使用主要是靠修飾器@responses.activate
實(shí)現(xiàn)setup和teardown,以及responses.add()
加入期望的返回值
import responses
import requests
from unittest import TestCase
from requests.exceptions import ConnectionError
class Demo(TestCase):
#進(jìn)行responses的setup和teardown
@responses.activate
def test_basic(self):
#確定response的詳細(xì)內(nèi)容
responses.add(responses.GET,
'https://nbaplayerprofile.com/api/1/kawhi_leonard',
json={
'team': 'San Antonio Spurs',
'personal': {
'DOB': '6/29/1991',
'Ht': '67',
'Wt': '230'
}
},
status=200)
expected_Kawhi_profile = {
'team' : 'San Antonio Spurs',
'personal' : {
'DOB' : '6/29/1991',
'Ht' : '67',
'Wt' : '230'
}
}
#發(fā)送了兩次請求
resp = requests.get('https://nbaplayerprofile.com/api/1/kawhi_leonard')
resp1 = requests.get('https://nbaplayerprofile.com/api/1/kawhi_leonard')
#對獲得的response進(jìn)行驗(yàn)證
self.assertEqual(resp.status_code, 200)
self.assertEqual(resp1.json(), expected_Kawhi_profile)
#responses.calls會記錄所有的請求響應(yīng)情況,可以利用起來進(jìn)行驗(yàn)證
self.assertEqual(len(responses.calls), 2)
self.assertEqual(responses.calls[0].request.url, 'https://nbaplayerprofile.com/api/1/kawhi_leonard')
self.assertEqual(responses.calls[1].response.text, '{"team": "San Antonio Spurs", "personal": {"DOB": "6/29/1991", "Ht": "67", "Wt": "230"}}')
# 錯誤的請求地址 驗(yàn)證會產(chǎn)生ConnectionError
with self.assertRaises(ConnectionError):
requests.get('https://nbaplayerprofile.com/api/1/')
@responses.activate
def test_error(self):
#響應(yīng)的body設(shè)置為Exception(),模擬出錯的情況
responses.add(responses.GET,
'https://nbaplayerprofile.com/api/1/error',
body = Exception(),
status=500
)
#驗(yàn)證異常被raise
with self.assertRaises(Exception):
requests.get('https://nbaplayerprofile.com/api/1/error')
我們可以直接把響應(yīng)寫在add()
里,也可以按照下面的方式傳遞:
import responses
responses.add(
responses.Response(
method='GET',
url='http://example.com',
),
)
Dynamic Responses
如果我們不想要返回固定的body,而是根據(jù)請求的不同返回不同的body,這個就需要responses.add_callback()
功能。具體做法很簡單,寫一個callback方法處理request,然后返回一個tuple(status, headers, body),然后callback方法作為參數(shù)傳入到responses.add_callback()
中,相當(dāng)于設(shè)置好了返回的status code, header和body。
@responses.activate
def test_dynamic_responses_text(self):
#定義callback方法,request請求會作為參數(shù)傳入方法體,進(jìn)行處理
def request_callback(request):
headers = {}
return (200, headers, str(request.body) + " this is from dynamic")
#使用add_callback()定義請求,callback關(guān)鍵參數(shù)傳入處理方法
responses.add_callback(responses.POST,
"https://nbaplayerprofile.com/api/1/foo",
callback = request_callback,
content_type = 'text/plain'
)
resp = requests.post("https://nbaplayerprofile.com/api/1/foo", "I am request")
#驗(yàn)證響應(yīng)文本是否被動態(tài)處理,
self.assertEqual(resp.status_code, 200)
self.assertEqual(resp.headers, {'Content-Type': 'text/plain'})
self.assertEqual(resp.text, "I am request this is from dynamic")
@responses.activate
def test_dynamic_responses_json(self):
#這個例子處理json
def request_callback(request):
payload = json.loads(request.body)
headers = {'User-Agent': 'Firefox/12.0'}
payload.update({'result': 'pass'})
resp_body = payload
return (200, headers, json.dumps(resp_body))
responses.add_callback(responses.POST,
'https://nbaplayerprofile.com/api/1/createplayer',
callback=request_callback,
content_type='application/json')
request_json_body = {'name': 'Di', 'gender': 'male'}
resp = requests.post(
'https://nbaplayerprofile.com/api/1/createplayer',
json.dumps(request_json_body)
)
self.assertEqual(resp.json(), {"name": "Di", "gender": "male", "result": "pass"})
self.assertEqual(len(responses.calls), 1)
self.assertEqual(responses.calls[0].request.url, 'https://nbaplayerprofile.com/api/1/createplayer')
self.assertEqual(responses.calls[0].response.text, '{"name": "Di", "gender": "male", "result": "pass"}')
self.assertEqual(responses.calls[0].response.headers['User-Agent'], 'Firefox/12.0')
使用context manager來定義response
除了上面說的@responses.activate
和responses.add()
方法外,responses也支持with關(guān)鍵字,作為context manager來定義。
# !/usr/bin/env python
# -*- coding: utf-8 -*-os
import responses
import requests
from unittest import TestCase
#不需要使用@responses.activate
class Demo1(TestCase):
def test_context_manager(self):
#使用with關(guān)鍵字和responses.RequestsMock()
with responses.RequestsMock() as resp:
#同樣使用add()方法定義
resp.add(resp.GET,
'https://nbaplayerprofile.com/api/1/kawhi_leonard',
json={
'team': 'San Antonio Spurs',
'personal': {
'DOB': '6/29/1991',
'Ht': '67',
'Wt': '230'
}},
status=200,
content_type='application/json'
)
expected_Kawhi_profile = {
'team' : 'San Antonio Spurs',
'personal' : {
'DOB' : '6/29/1991',
'Ht' : '67',
'Wt' : '230'
}
}
# 發(fā)送了兩次請求
resps = requests.get('https://nbaplayerprofile.com/api/1/kawhi_leonard')
resps1 = requests.get('https://nbaplayerprofile.com/api/1/kawhi_leonard')
# 對獲得的response進(jìn)行驗(yàn)證
self.assertEqual(resps.status_code, 200)
self.assertEqual(resps1.json(), expected_Kawhi_profile)
# responses.calls會記錄所有的請求響應(yīng)情況,可以利用起來進(jìn)行驗(yàn)證
#這里的responses就是with關(guān)鍵字定義的名稱
self.assertEqual(len(resp.calls), 2)
self.assertEqual(resp.calls[0].request.url, 'https://nbaplayerprofile.com/api/1/kawhi_leonard')
self.assertEqual(resp.calls[1].response.text,
'{"team": "San Antonio Spurs", "personal": {"DOB": "6/29/1991", "Ht": "67", "Wt": "230"}}')
當(dāng)定義的mock service沒有被訪問的時候,AssertionError: Not all requests have been executed
會被raise,這時候需要設(shè)置參數(shù)assert_all_requests_are_fired=False
來避免這個異常。
def test_requests_fired(self):
# 設(shè)置assert_all_requests_are_fired來避免AssertionError: Not all requests have been executed`
with responses.RequestsMock(assert_all_requests_are_fired=False) as resp:
# 同樣使用add()方法定義
resp.add(resp.GET,
'https://nbaplayerprofile.com/api/1/kawhi_leonard',
json={
'team': 'San Antonio Spurs',
'personal': {
'DOB': '6/29/1991',
'Ht': '67',
'Wt': '230'
}},
status=200,
content_type='application/json'
)
同樣的,我們也可以定制化responses,這里需要使用response_callback
參數(shù)。
def test_callback(self):
#定義response_callback方法,接受一個responses為參數(shù),返回一個responses
#這個例子中我們給responses加了一個屬性
def response_callback(resp):
resp.result = 'pass'
return resp
with responses.RequestsMock(response_callback=response_callback) as stub:
stub.add(responses.GET,
"https://nbaplayerprofile.com/api/1/foo",
body="I am body"
)
resps = requests.get("https://nbaplayerprofile.com/api/1/foo", "I am request")
self.assertEqual(resps.text, "I am body")
#驗(yàn)證callback方法寫入的新屬性
self.assertEqual(resps.result, "pass")