Responses学习笔记

Responses

Responses - A utility for mocking out the Python Requests library,主要用于Web Service mocking。

安装

使用pip3 install responses

基本使用

responses的使用主要是靠修饰器@responses.activate实现setup和teardown,以及responses.add()加入期望的返回值

import responses
import requests
from unittest import TestCase
from requests.exceptions import ConnectionError


class Demo(TestCase):

    #进行responses的setup和teardown
    @responses.activate
    def test_basic(self):
        #确定response的详细内容
        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'
                          }
                      }
        #发送了两次请求
        resp = requests.get('https://nbaplayerprofile.com/api/1/kawhi_leonard')
        resp1 = requests.get('https://nbaplayerprofile.com/api/1/kawhi_leonard')

        #对获得的response进行验证
        self.assertEqual(resp.status_code, 200)
        self.assertEqual(resp1.json(), expected_Kawhi_profile)
        #responses.calls会记录所有的请求响应情况,可以利用起来进行验证
        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"}}')

        # 错误的请求地址 验证会产生ConnectionError
        with self.assertRaises(ConnectionError):
            requests.get('https://nbaplayerprofile.com/api/1/')

    @responses.activate
    def test_error(self):
        #响应的body设置为Exception(),模拟出错的情况
        responses.add(responses.GET,
                      'https://nbaplayerprofile.com/api/1/error',
                      body = Exception(),
                      status=500
        )
        #验证异常被raise
        with self.assertRaises(Exception):
           requests.get('https://nbaplayerprofile.com/api/1/error')

我们可以直接把响应写在add()里,也可以按照下面的方式传递:

import responses

responses.add(
    responses.Response(
        method='GET',
        url='http://example.com',
    ),
)

Dynamic Responses

如果我们不想要返回固定的body,而是根据请求的不同返回不同的body,这个就需要responses.add_callback()功能。具体做法很简单,写一个callback方法处理request,然后返回一个tuple(status, headers, body),然后callback方法作为参数传入到responses.add_callback()中,相当于设置好了返回的status code, header和body。

    @responses.activate
    def test_dynamic_responses_text(self):
        #定义callback方法,request请求会作为参数传入方法体,进行处理
        def request_callback(request):
            headers = {}
            return (200, headers, str(request.body) + " this is from dynamic")
        #使用add_callback()定义请求,callback关键参数传入处理方法
        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")
        #验证响应文本是否被动态处理,
        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关键字,作为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关键字和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'
                          }
                      }

            # 发送了两次请求
            resps = requests.get('https://nbaplayerprofile.com/api/1/kawhi_leonard')
            resps1 = requests.get('https://nbaplayerprofile.com/api/1/kawhi_leonard')

            # 对获得的response进行验证
            self.assertEqual(resps.status_code, 200)
            self.assertEqual(resps1.json(), expected_Kawhi_profile)

            # responses.calls会记录所有的请求响应情况,可以利用起来进行验证
            #这里的responses就是with关键字定义的名称
            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"}}')

当定义的mock service没有被访问的时候,AssertionError: Not all requests have been executed会被raise,这时候需要设置参数assert_all_requests_are_fired=False来避免这个异常。

def test_requests_fired(self):
    # 设置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参数。

def test_callback(self):
    #定义response_callback方法,接受一个responses为参数,返回一个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")
        #验证callback方法写入的新属性
        self.assertEqual(resps.result, "pass")
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 203,547评论 6 477
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,399评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 150,428评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,599评论 1 274
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,612评论 5 365
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,577评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,941评论 3 395
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,603评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,852评论 1 297
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,605评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,693评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,375评论 4 318
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,955评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,936评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,172评论 1 259
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 43,970评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,414评论 2 342

推荐阅读更多精彩内容

  • Spring Cloud为开发人员提供了快速构建分布式系统中一些常见模式的工具(例如配置管理,服务发现,断路器,智...
    卡卡罗2017阅读 134,587评论 18 139
  • https://nodejs.org/api/documentation.html 工具模块 Assert 测试 ...
    KeKeMars阅读 6,297评论 0 6
  • scrapy学习笔记(有示例版) 我的博客 scrapy学习笔记1.使用scrapy1.1创建工程1.2创建爬虫模...
    陈思煜阅读 12,653评论 4 46
  • 那双“梦蝶”旧了,总想着添双新的,直到遇见这双荷花样式的绣鞋。它很特别,鞋面挂着铃铛,走起路来“叮铃叮铃叮铃”……...
    苏长亭阅读 456评论 85 30
  • 我今天发烧了,头疼的特别厉害!书上的字我没有写,所以我们组就没有达标,我很难过。 我本来...
    王启萱阅读 192评论 0 0