从测试视图 Python 中模拟外部 API POST 调用

Ada*_*dam 4 python unit-testing python-requests python-unittest python-unittest.mock

views.py我有一个外部 API POST 调用,该调用是从我的内部进行的:

class MyView(APIView):
  def post(self, request):
    my_headers = {
      "Content-Type": "application/json"
    }
    response = requests.post("https://some-external-api.com", data=json.dumps(request.data), headers=my_headers)

    return Response(status.response.status_code)
Run Code Online (Sandbox Code Playgroud)

正如您所看到的,这是一个非常简单的情况,使用视图端点接收到的相同数据对外部 API 进行 POST 调用。

现在,我正在尝试为此创建一个单元测试,同时模拟来自“https://some-external-api.com”的响应,因此我显然不必每次都对该单元进行实际调用测试运行。但我遇到了困难,因为我无法让模拟方面正常工作,并且每次请求都发送到实际的外部端点。

我知道网上有很多例子,但我尝试过的似乎都不起作用。我还没有看到模拟响应应该来自视图文件本身的示例。截至目前,我有这个:

@patch('requests.post')
def test_external_api_call(self, mock_post)
  mock_post.return_value.ok = True
  response = self.client.post(reverse('my-view'), {
    //my random dummy json object goes here
  }, format='json')

  self.assertEqual(response.status_code, 200)
Run Code Online (Sandbox Code Playgroud)

正如我所提到的,使用上面的代码,实际调用了“https://some-external-api.com”,而不是被嘲笑。

Nie*_*ano 5

无需重新发明轮子,只需使用请求库的可用模拟程序,例如requests_mock

import json

import pytest
import requests
import requests_mock  # python3 -m pip install requests-mock


def post():
    my_headers = {"Content-Type": "application/json"}
    my_data = {"some_key": "some_value"}

    response = requests.post("https://some-external-api.com", data=json.dumps(my_data), headers=my_headers)

    print(response.status_code, response.json())


@pytest.fixture
def mock_post():
    with requests_mock.Mocker() as requests_mocker:
        def match_data(request):
            """
            This is just optional. Remove if not needed. This will check if the request contains the expected body.
            """
            return request.json() == {"some_key": "some_value"}

        requests_mocker.post(
            "https://some-external-api.com",  # Match the target URL.
            additional_matcher=match_data,  # Optional. If you want to match the request body too.
            status_code=200,  # The status code of the response.
            json={"the_result": "was successful!"},  # Optional. The value when .json() is called on the response.
        )

        yield


def test_requests(mock_post):
    post()
Run Code Online (Sandbox Code Playgroud)
$ pytest -q -rP
================================================================================================= PASSES ==================================================================================================
______________________________________________________________________________________________ test_requests ______________________________________________________________________________________________
------------------------------------------------------------------------------------------ Captured stdout call -------------------------------------------------------------------------------------------
200 {'the_result': 'was successful!'}
1 passed in 0.04s
Run Code Online (Sandbox Code Playgroud)