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

Mock external API POST call in view from test view Python

我有一个外部 API POST 调用是从我的 views.py 内部发出的:

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)

如您所见,这是一个非常简单的案例,使用接收到视图端点的相同数据对外部 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)

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

无需重新发明轮子,只需使用请求库可用的模拟程序,例如 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()
$ pytest -q -rP
================================================================================================= PASSES ==================================================================================================
______________________________________________________________________________________________ test_requests ______________________________________________________________________________________________
------------------------------------------------------------------------------------------ Captured stdout call -------------------------------------------------------------------------------------------
200 {'the_result': 'was successful!'}
1 passed in 0.04s