我试图使用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调用,那么这个过程是最好的

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

其他回答

以下是对我有效的方法:

import mock
@mock.patch('requests.get', mock.Mock(side_effect = lambda k:{'aurl': 'a response', 'burl' : 'b response'}.get(k, 'unhandled request %s'%k)))

对于pytest用户,有一个来自https://pypi.org/project/pytest-responsemock/的方便的fixture

例如,模拟GET到http://some.domain,你可以:

def test_me(response_mock):

    with response_mock('GET http://some.domain -> 200 :Nice'):
        response = send_request()
        assert result.ok
        assert result.content == b'Nice'

这对我来说是可行的,尽管我还没有做太多复杂的测试。

import json
from requests import Response

class MockResponse(Response):
    def __init__(self,
                 url='http://example.com',
                 headers={'Content-Type':'text/html; charset=UTF-8'},
                 status_code=200,
                 reason = 'Success',
                 _content = 'Some html goes here',
                 json_ = None,
                 encoding='UTF-8'
                 ):
    self.url = url
    self.headers = headers
    if json_ and headers['Content-Type'] == 'application/json':
        self._content = json.dumps(json_).encode(encoding)
    else:
        self._content = _content.encode(encoding)

    self.status_code = status_code
    self.reason = reason
    self.encoding = encoding

然后你可以创建响应:

mock_response = MockResponse(
    headers={'Content-Type' :'application/json'},
    status_code=401,
    json_={'success': False},
    reason='Unauthorized'
)
mock_response.raise_for_status()

给了

requests.exceptions.HTTPError: 401 Client Error: Unauthorized for url: http://example.com

为了避免安装其他依赖项,您应该创建一个假响应。这个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 ===============================