0

我正在学习对我的 FastAPI 端点使用模拟测试。我对这个非常基本的问题感到困惑:如果没有进行实际的 HTTP 调用,模拟测试如何实际测试 API 响应?

我知道通过模仿预期的响应,我们可以避免调用实际的 API。但我想测试 API 是否可访问、响应是否正确、并发调用是否受限、是否存在其他授权问题等。

或者,仅在未测试 API 调用本身时才建议进行模拟测试(即测试具有 API 调用的某些其他功能)。

4

1 回答 1

0

如果没有进行实际的 HTTP 调用,模拟测试如何实际测试 API 响应?

他们没有。

...是否仅在未测试 API 调用本身时才建议进行模拟测试(即测试具有 API 调用的某些其他功能)?

简而言之,的。
它特别用于单元测试中,用于测试代码的特定部分。

如果你的目标是

...测试 API 是否可访问、响应是否正确、并发调用限制、任何其他授权问题等。

然后,模拟您的 API 调用和使用模拟库绝对不是您需要的解决方案。您正在寻找的是端到端测试领域的某个地方,它验证代码/API 在现实条件下如何工作,例如从实际用户的角度。在这种情况下,您需要实际调用 API。

使用模拟 API 调用进行测试的目的不是测试实际的 API,而是测试围绕或依赖于 API 调用的代码的行为/逻辑,例如您的代码如何处理从 API 接收不同类型的响应或什么您在成功调用 API 后处理响应。它允许您编写可重现的测试,清楚地定义/指定代码的预期行为方式(相关:TDD),而无需重复调用 API 或依赖外部依赖项(例如设置 API 身份验证等)。

模拟 API 调用的另一个优点是,即使在没有网络连接或没有正确 API 访问凭证的本地或隔离环境中,它也允许您重复运行测试。这对于可能在某处持续运行并且可能无法访问您的 API 的 CI 管道特别有用。

让我们以这段代码为例:

import json
import requests
from requests import Response

def call_api() -> bool:
    res: Response = requests.post(API_URL, json={"quantity": 1})

    if res.status_code != 200:
        raise Exception(f"API returned an error: {res.text}")

    try:
        message = res.json()["message"]
    except (json.JSONDecodeError, KeyError):
        raise Exception("API returned unknown response format")

    return message

它调用一个 API,期望一个带有"message"键的 JSON 响应,并返回该"message"响应的那一部分。然后,它包括一些错误处理,用于 API 未按预期响应的情况(为简化起见,它只是引发了一个 bare Exception)。

为这些错误情况编写测试的一种易于重现的方法是模拟响应。

import pytest
import requests_mock

@pytest.mark.parametrize("status_code", [401, 403, 500])
def test_call_api_but_returned_not_200(status_code: int):
    """Expect our code to raise an Exception if it's not a 200"""

    with requests_mock.Mocker() as mocker:
        mocker.post(API_URL, status_code=status_code, text="API Error")

        with pytest.raises(Exception) as exc:
            call_api()

    # Expect that the Exception contains the exact error in the response
    assert "API Error" in str(exc.value)


def test_call_api_but_returned_unknown_format():
    """Expect our code to raise an Exception if we can't handle the response format"""
    with requests_mock.Mocker() as mocker:
        mocker.post(API_URL, status_code=200, json={"unexpected": "keys"})

        with pytest.raises(Exception) as exc:
            call_api()

    assert "API returned unknown response format" in str(exc.value)

在上述测试中,您不需要实际调用 API,因为 1)您可能无法始终如一地重现这些错误情况,以及 2)您实际上并不需要响应中的确切属性,只要在Response对象。

实际上,即使对于成功案例,模拟也很有用,只需定义该特定函数返回"message"API 响应属性中设置的任何内容(再次,相关:TDD)。

def test_call_api_and_return_message():
    """Expect our code to return the `message` attr as-is"""
    with requests_mock.Mocker() as mocker:
        mocker.post(API_URL, status_code=200, json={"message": "YeS, it worked!"})

        result = call_api()

    assert result == "YeS, it worked!"
于 2022-02-19T06:03:25.550 回答