我试图使用python模拟包来模拟python请求模块。让我在下面的场景中工作的基本调用是什么?
在views.py中,我有一个函数,它每次都以不同的响应进行各种request .get()调用
def myview(request):
res1 = requests.get('aurl')
res2 = request.get('burl')
res3 = request.get('curl')
在我的测试类中,我想做类似的事情,但不能确定确切的方法调用
步骤1:
# Mock the requests module
# when mockedRequests.get('aurl') is called then return 'a response'
# when mockedRequests.get('burl') is called then return 'b response'
# when mockedRequests.get('curl') is called then return 'c response'
步骤2:
调用我的视图
步骤3:
验证响应包含'a response', 'b response', 'c response'
我如何完成第1步(模拟请求模块)?
我将添加这些信息,因为我很难弄清楚如何模拟异步api调用。
以下是我模拟异步调用所做的工作。
这是我想测试的函数
async def get_user_info(headers, payload):
return await httpx.AsyncClient().post(URI, json=payload, headers=headers)
您仍然需要MockResponse类
class MockResponse:
def __init__(self, json_data, status_code):
self.json_data = json_data
self.status_code = status_code
def json(self):
return self.json_data
添加MockResponseAsync类
class MockResponseAsync:
def __init__(self, json_data, status_code):
self.response = MockResponse(json_data, status_code)
async def getResponse(self):
return self.response
下面是测试。这里重要的是,我之前创建了响应,因为init函数不能是异步的,对getResponse的调用是异步的,所以它都签出了。
@pytest.mark.asyncio
@patch('httpx.AsyncClient')
async def test_get_user_info_valid(self, mock_post):
"""test_get_user_info_valid"""
# Given
token_bd = "abc"
username = "bob"
payload = {
'USERNAME': username,
'DBNAME': 'TEST'
}
headers = {
'Authorization': 'Bearer ' + token_bd,
'Content-Type': 'application/json'
}
async_response = MockResponseAsync("", 200)
mock_post.return_value.post.return_value = async_response.getResponse()
# When
await api_bd.get_user_info(headers, payload)
# Then
mock_post.return_value.post.assert_called_once_with(
URI, json=payload, headers=headers)
如果你有更好的方法告诉我,不过我觉得这样很干净。
对于那些不想为pytest安装额外库的人,这里有一个例子。我将在这里复制一些扩展,基于上面的例子:
import datetime
import requests
class MockResponse:
def __init__(self, json_data, status_code):
self.json_data = json_data
self.status_code = status_code
self.elapsed = datetime.timedelta(seconds=1)
# mock json() method always returns a specific testing dictionary
def json(self):
return self.json_data
def test_get_json(monkeypatch):
# Any arguments may be passed and mock_get() will always return our
# mocked object, which only has the .json() method.
def mock_get(*args, **kwargs):
return MockResponse({'mock_key': 'mock_value'}, 418)
# apply the monkeypatch for requests.get to mock_get
monkeypatch.setattr(requests, 'get', mock_get)
# app.get_json, which contains requests.get, uses the monkeypatch
response = requests.get('https://fakeurl')
response_json = response.json()
assert response_json['mock_key'] == 'mock_value'
assert response.status_code == 418
assert response.elapsed.total_seconds() == 1
============================= test session starts ==============================
collecting ... collected 1 item
test_so.py::test_get_json PASSED [100%]
============================== 1 passed in 0.07s ===============================
您可以使用request -mock代替吗?
假设你的myview函数接受一个请求。Session对象,用它发出请求,并对输出做一些事情:
# mypackage.py
def myview(session):
res1 = session.get("http://aurl")
res2 = session.get("http://burl")
res3 = session.get("http://curl")
return f"{res1.text}, {res2.text}, {res3.text}"
# test_myview.py
from mypackage import myview
import requests
def test_myview(requests_mock):
# set up requests
a_req = requests_mock.get("http://aurl", text="a response")
b_req = requests_mock.get("http://burl", text="b response")
c_req = requests_mock.get("http://curl", text="c response")
# test myview behaviour
session = requests.Session()
assert myview(session) == "a response, b response, c response"
# check that requests weren't called repeatedly
assert a_req.called_once
assert b_req.called_once
assert c_req.called_once
assert requests_mock.call_count == 3
你也可以在Pytest之外的框架中使用requests_mock——文档非常棒。
解决请求的一个可能的方法是使用库betamax,它记录所有的请求,之后如果你在相同的url中使用相同的参数发出请求,betamax将使用记录的请求,我一直在用它来测试网络爬虫,它节省了我很多时间。
import os
import requests
from betamax import Betamax
from betamax_serializers import pretty_json
WORKERS_DIR = os.path.dirname(os.path.abspath(__file__))
CASSETTES_DIR = os.path.join(WORKERS_DIR, u'resources', u'cassettes')
MATCH_REQUESTS_ON = [u'method', u'uri', u'path', u'query']
Betamax.register_serializer(pretty_json.PrettyJSONSerializer)
with Betamax.configure() as config:
config.cassette_library_dir = CASSETTES_DIR
config.default_cassette_options[u'serialize_with'] = u'prettyjson'
config.default_cassette_options[u'match_requests_on'] = MATCH_REQUESTS_ON
config.default_cassette_options[u'preserve_exact_body_bytes'] = True
class WorkerCertidaoTRT2:
session = requests.session()
def make_request(self, input_json):
with Betamax(self.session) as vcr:
vcr.use_cassette(u'google')
response = session.get('http://www.google.com')
https://betamax.readthedocs.io/en/latest/