我试图使用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步(模拟请求模块)?
下面是一个带有请求响应类的解决方案。恕我直言,它更干净。
import json
from unittest.mock import patch
from requests.models import Response
def mocked_requests_get(*args, **kwargs):
response_content = None
request_url = kwargs.get('url', None)
if request_url == 'aurl':
response_content = json.dumps('a response')
elif request_url == 'burl':
response_content = json.dumps('b response')
elif request_url == 'curl':
response_content = json.dumps('c response')
response = Response()
response.status_code = 200
response._content = str.encode(response_content)
return response
@mock.patch('requests.get', side_effect=mocked_requests_get)
def test_fetch(self, mock_get):
response = requests.get(url='aurl')
assert ...
我将演示如何通过将真正的请求与返回相同数据的假请求交换来将编程逻辑与实际的外部库分离。在你看来,如果外部api调用,那么这个过程是最好的
import pytest
from unittest.mock import patch
from django.test import RequestFactory
@patch("path(projectname.appname.filename).requests.post")
def test_mock_response(self, mock_get, rf: RequestFactory):
mock_get.return_value.ok = Mock(ok=True)
mock_get.return_value.status_code = 400
mock_get.return_value.json.return_value = {you can define here dummy response}
request = rf.post("test/", data=self.payload)
response = view_name_view(request)
expected_response = {
"success": False,
"status": "unsuccessful",
}
assert response.data == expected_response
assert response.status_code == 400
为了避免安装其他依赖项,您应该创建一个假响应。这个FakeResponse可以是Response的子类(我认为这是一个很好的方法,因为它更现实),或者只是一个具有您需要的属性的简单类。
简单的假类
class FakeResponse:
status_code = None
def __init__(self, *args, **kwargs):
self.status_code = 500
self.text = ""
回应之子
class FakeResponse(Response):
encoding = False
_content = None
def __init__(*args, **kwargs):
super(FakeResponse).__thisclass__.status_code = 500
# Requests requires to be not be None, if not throws an exception
# For reference: https://github.com/psf/requests/issues/3698#issuecomment-261115119
super(FakeResponse).__thisclass__.raw = io.BytesIO()
对于那些不想为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 ===============================
使用requests_mock可以很容易地修补任何请求
pip install requests-mock
from unittest import TestCase
import requests_mock
from <yourmodule> import <method> (auth)
class TestApi(TestCase):
@requests_mock.Mocker()
def test_01_authentication(self, m):
"""Successful authentication using username password"""
token = 'token'
m.post(f'http://localhost/auth', json= {'token': token})
act_token =auth("user", "pass")
self.assertEqual(act_token, token)