python mock测试_Python mock http requests 单元测试用例测试

在写 Python Mock 的 requests测试用例,需要mock对方服务的http返回包行为,方便进行调试。而如果对方还没准备好,或者有副作用,又不敢直接请求。因此我们需要一个 Mock 的功能。

以前使用 Python 自带的 Mock 类库来进行,感觉写起来总感觉有点不顺畅。

在网上找到了一个类库 requests_mock ,用起来很方便,这里给大家分享一下。

requests 类库有一个可插件化的传输层适配器,并且允许你根据不同的url或者协议注册自己的handler。requests-mock的核心就是一个简单的被提前加载的传输层适配器。

安装 requests_mock

pip install requests_mock

使用 Mocker

通过 Context Manager方法使用

import requests

import requests_mock

with requests_mock.Mocker() as m:

… m.get(‘http://test.com’, text=‘resp’)

… requests.get(‘http://test.com’).text

‘resp’

通过装饰器使用

@requests_mock.Mocker()

… def test_function(m):

… m.get(‘http://test.com’, text=‘resp’)

… return requests.get(‘http://test.com’).text

test_function()

‘resp’

这种方法需要在参数的最后一个位置进行声明,会传递进来这个对象。

如果有冲突,可以提前指定好。例如

@requests_mock.Mocker(kw=‘mock’)

… def test_kw_function(**kwargs):

… kwargs[‘mock’].get(‘http://test.com’, text=‘resp’)

… return requests.get(‘http://test.com’).text

test_kw_function()

‘resp’

类装饰器

requests_mock.Mocker.TEST_PREFIX = ‘foo’

@requests_mock.Mocker()

… class Thing(object):

… def foo_one(self, m):

… m.register_uri(‘GET’, ‘http://test.com’, text=‘resp’)

… return requests.get(‘http://test.com’).text

… def foo_two(self, m):

… m.register_uri(‘GET’, ‘http://test.com’, text=‘resp’)

… return requests.get(‘http://test.com’).text

Thing().foo_one()

‘resp’

Thing().foo_two()

‘resp’

类似 unitest 一样寻找测试函数开头前缀, requests_mock 也是类似的寻找测试函数,不过不想test前缀开头,可以用requests_mock.Mocker.TEST_PREFIX 来指定。

请求真实的HTTP服务

通过real_http关键字,可以请求真实的 HTTP 服务。

with requests_mock.Mocker(real_http=True) as m:

… m.register_uri(‘GET’, ‘http://test.com’, text=‘resp’)

… print(requests.get(‘http://test.com’).text)

… print(requests.get(‘http://www.google.com’).status_code)

‘resp’

200

或者

with requests_mock.Mocker() as m:

… m.register_uri(‘GET’, ‘http://test.com’, text=‘resp’)

… m.register_uri(‘GET’, ‘http://www.google.com’, real_http=True)

… print(requests.get(‘http://test.com’).text)

… print(requests.get(‘http://www.google.com’).status_code)

‘resp’

200

Mock url 的方法

其实上面已经简单提到了,主要是用

adapter.register_uri(‘GET’, url, …)

匹配具体的url地址

只要协议和url地址都匹配上,才有效。

… >>> adapter.register_uri(‘GET’, ‘mock://test.com/path’, text=‘resp’)

… >>> session.get(‘mock://test.com/path’).text

… ‘resp’

url中的path 路径匹配

比如不管协议,主要域名匹配上就行。

… >>> adapter.register_uri(‘GET’, ‘//test.com/’, text=‘resp’)

… >>> session.get(‘mock://test.com/’).text

… ‘resp’

或者主要url中的path匹配就行

… >>> adapter.register_uri(‘GET’, ‘/path’, text=‘resp’)

… >>> session.get(‘mock://test.com/path’).text

… ‘resp’

… >>> session.get(‘mock://another.com/path’).text

… ‘resp’

匹配url中的 query 参数部分

adapter.register_uri(‘GET’, ‘/7?a=1’, text=‘resp’)

session.get(‘mock://test.com/7?a=1&b=2’).text

‘resp’

匹配任意 http method,比如get、 post

adapter.register_uri(requests_mock.ANY, ‘mock://test.com/8’, text=‘resp’)

session.get(‘mock://test.com/8’).text

‘resp’

session.post(‘mock://test.com/8’).text

‘resp’

下面是无脑什么url地址,直接返回指定的response

adapter.register_uri(requests_mock.ANY, requests_mock.ANY, text=‘resp’)

session.get(‘mock://whatever/you/like’).text

‘resp’

session.post(‘mock://whatever/you/like’).text

‘resp’

动态返回 response

通过上面的动态匹配 url 地址,接下来可能多次请求一个url地址需要返回不同的数据。

主要还是利用上面的 requests_mock.Adapter.register_uri() 这个函数来支持。

注册 Reponses

register_uri 用于模拟http的请求。 主要有参数控制 response的一些 header 信息。

status_code: The HTTP status response to return. Defaults to 200.

reason: The reason text that accompanies the Status (e.g. ‘OK’ in ‘200 OK’)

headers: A dictionary of headers to be included in the response.

cookies: A CookieJar containing all the cookies to add to the response.

还有控制body的的参数

json: A python object that will be converted to a JSON string.

text: A unicode string. This is typically what you will want to use for regular textual content.

content: A byte string. This should be used for including binary data in responses.

body: A file like object that contains a .read() function.

raw: A prepopulated urllib3.response.HTTPResponse to be returned.

exc: An exception that will be raised instead of returning a response.

这些参数都是 requests.Response 对象的成员变量。

使用示例

adapter.register_uri(‘GET’, ‘mock://test.com/1’, json={‘a’: ‘b’}, status_code=200)

resp = session.get(‘mock://test.com/1’)

resp.json()

{‘a’: ‘b’}

adapter.register_uri(‘GET’, ‘mock://test.com/2’, text=‘Not Found’, status_code=404)

resp = session.get(‘mock://test.com/2’)

resp.text

‘Not Found’

resp.status_code

404

动态响应

requests_mock 提供了一个回调函数,用于进行动态判断。

def callback(request, context):

request请求的对象和,context是返回的response对象

request: The requests.Request object that was provided.

context: An object containing the collected known data about this response.

使用示例

def text_callback(request, context):

… context.status_code = 200

… context.headers[‘Test1’] = ‘value1’

… return ‘response’

adapter.register_uri(‘GET’,

… ‘mock://test.com/3’,

… text=text_callback,

… headers={‘Test2’: ‘value2’},

… status_code=400)

resp = session.get(‘mock://test.com/3’)

resp.status_code, resp.headers, resp.text

(200, {‘Test1’: ‘value1’, ‘Test2’: ‘value2’}, ‘response’)

按指定list结果返回

提前写好需要返回的 response 列表。依次返回。

adapter.register_uri(‘GET’, ‘mock://test.com/4’, [{‘text’: ‘resp1’, ‘status_code’: 300},

… {‘text’: ‘resp2’, ‘status_code’: 200}])

resp = session.get(‘mock://test.com/4’)

(resp.status_code, resp.text)

(300, ‘resp1’)

resp = session.get(‘mock://test.com/4’)

(resp.status_code, resp.text)

(200, ‘resp2’)

resp = session.get(‘mock://test.com/4’)

(resp.status_code, resp.text)

(200, ‘resp2’)

demo 1 : 需要请求获取任务id,然后查询运行中,然后成功的场景

import json

import time

from unittest import TestCase

from threading import Thread

import requests_mock

def task_result_success(m):

“”“想要成功的时候的返回包”""

time.sleep(0.05)

resp = {“data”: {},

“code”: “OK”}

m.register_uri(requests_mock.ANY, requests_mock.ANY, text=json.dumps(resp))

@requests_mock.Mocker()

def test(m):

resp = {“code”: “OK”,

“data”: {“task_id”: “xxxxx”}

}

注册结果,直接返回需要的任务id

m.register_uri(requests_mock.ANY, requests_mock.ANY, text=json.dumps(resp))

Thread(target=task_result_success, args=(m,)).start() # 过一会后,mock对象的返回包会被替代。

your.dosomething() # 自己的业务逻辑,这里会请求获得一个任务ID,然后查询直接返回成功。

这里通过 pytest 运行了 test() 函数,先对http请求返回一个任务id的返回包。然后开启一个线程,过0.05s后,返回一个成功的请求包。

因为执行了 your.dosomething() 会阻塞主线程,所以通过一个子线程来修改 Mocker 对象的返回。想了好久想到的这么一个方法。

demo 2: 也是轮训taskid,然后返回的running,然后success

定义返回的顺序

response_list = [

{‘json’: get_task_id_resp(), },

{‘json’: get_task_query_running_resp(), },

{‘json’: get_task_query_success_resp(), }

]

注册

m.register_uri(requests_mock.ANY, requests_mock.ANY, response_list)

执行调用代码开始测试,观察log

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
当然可以,Mock 测试可以模拟复杂的场景和数据,来验证代码的正确性和健壮性。 以下是一个例子,假设有一个函数 `get_data`,它从一个 API 中获取数据并返回。我们需要对这个函数进行 Mock 测试。 ```python import requests def get_data(): response = requests.get('http://example.com/api/data') if response.status_code == 200: return response.json() else: return None ``` 我们可以使用 `unittest.mock` 模块来创建一个 Mock 对象,模拟 API 的响应数据。例如,我们可以创建一个 `MockResponse` 类来模拟响应数据: ```python class MockResponse: def __init__(self, data=None, status_code=200): self.data = data self.status_code = status_code def json(self): return self.data ``` 然后,我们可以使用 Mock 对象来测试 `get_data` 函数,例如,我们可以测试当 API 响应成功时的情况: ```python from unittest.mock import patch @patch('requests.get') def test_get_data(mock_get): mock_response = MockResponse(data={'key': 'value'}) mock_get.return_value = mock_response data = get_data() assert data == {'key': 'value'} ``` 这个测试用例使用 `unittest.mock.patch` 装饰器来创建一个 Mock 对象,并将其传递给 `requests.get` 函数。然后,我们可以运行 `get_data` 函数,并检查返回值是否与我们预期的值相同。 当然,这只是一个简单的例子,Mock 测试可以模拟更复杂的场景和数据,以验证代码的正确性和健壮性。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值