我试图使用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 ...

其他回答

解决请求的一个可能的方法是使用库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/

我将演示如何通过将真正的请求与返回相同数据的假请求交换来将编程逻辑与实际的外部库分离。在你看来,如果外部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

这就是模拟请求的方法。Post,将其更改为HTTP方法

@patch.object(requests, 'post')
def your_test_method(self, mockpost):
    mockresponse = Mock()
    mockpost.return_value = mockresponse
    mockresponse.text = 'mock return'

    #call your target method now

对于那些不想为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 ===============================

下面是一个带有请求响应类的解决方案。恕我直言,它更干净。

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 ...