Responses學(xué)習(xí)筆記

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.activateresponses.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")
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 227,702評論 6 531
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 98,143評論 3 415
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事?!?“怎么了?”我有些...
    開封第一講書人閱讀 175,553評論 0 373
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經(jīng)常有香客問我,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 62,620評論 1 307
  • 正文 為了忘掉前任,我火速辦了婚禮,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 71,416評論 6 405
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 54,940評論 1 321
  • 那天,我揣著相機(jī)與錄音,去河邊找鬼。 笑死,一個胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 43,024評論 3 440
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 42,170評論 0 287
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 48,709評論 1 333
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 40,597評論 3 354
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 42,784評論 1 369
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 38,291評論 5 357
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 44,029評論 3 347
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 34,407評論 0 25
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 35,663評論 1 280
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個月前我還...
    沈念sama閱讀 51,403評論 3 390
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 47,746評論 2 370

推薦閱讀更多精彩內(nèi)容

  • Spring Cloud為開發(fā)人員提供了快速構(gòu)建分布式系統(tǒng)中一些常見模式的工具(例如配置管理,服務(wù)發(fā)現(xiàn),斷路器,智...
    卡卡羅2017閱讀 134,776評論 18 139
  • https://nodejs.org/api/documentation.html 工具模塊 Assert 測試 ...
    KeKeMars閱讀 6,356評論 0 6
  • scrapy學(xué)習(xí)筆記(有示例版) 我的博客 scrapy學(xué)習(xí)筆記1.使用scrapy1.1創(chuàng)建工程1.2創(chuàng)建爬蟲模...
    陳思煜閱讀 12,740評論 4 46
  • 那雙“夢蝶”舊了,總想著添雙新的,直到遇見這雙荷花樣式的繡鞋。它很特別,鞋面掛著鈴鐺,走起路來“叮鈴叮鈴叮鈴”……...
    蘇長亭閱讀 470評論 85 30
  • 我今天發(fā)燒了,頭疼的特別厲害!書上的字我沒有寫,所以我們組就沒有達(dá)標(biāo),我很難過。 我本來...
    王啟萱閱讀 201評論 0 0