我正在学习对我的 FastAPI 端点使用模拟测试。我对这个非常基本的问题感到困惑:如果没有进行实际的 HTTP 调用,模拟测试如何实际测试 API 响应?
我知道通过模仿预期的响应,我们可以避免调用实际的 API。但我想测试 API 是否可访问、响应是否正确、并发调用是否受限、是否存在其他授权问题等。
或者,仅在未测试 API 调用本身时才建议进行模拟测试(即测试具有 API 调用的某些其他功能)。
我正在学习对我的 FastAPI 端点使用模拟测试。我对这个非常基本的问题感到困惑:如果没有进行实际的 HTTP 调用,模拟测试如何实际测试 API 响应?
我知道通过模仿预期的响应,我们可以避免调用实际的 API。但我想测试 API 是否可访问、响应是否正确、并发调用是否受限、是否存在其他授权问题等。
或者,仅在未测试 API 调用本身时才建议进行模拟测试(即测试具有 API 调用的某些其他功能)。
如果没有进行实际的 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!"