简体   繁体   English

mock testing REST API如何在没有调用实际API的情况下测试API?

[英]How does mock testing REST APIs test the API when the actual API is not called?

I am learning to use mock tests for my FastAPI endpoints.我正在学习对我的 FastAPI 端点使用模拟测试。 And I am puzzled by this very basic question: how does mock test actually test an API response if the actual HTTP call is not made?我对这个非常基本的问题感到困惑:如果没有进行实际的 HTTP 调用,模拟测试如何实际测试 API 响应?

I understand that by mimicking the expected response, we can avoid calling the actual API. But I would want to test if the API is accessible, that the response is correct, whether concurrent calls are limited, if there are any other authorization problems, etc.我知道通过模仿预期的响应,我们可以避免调用实际的 API。但我想测试 API 是否可访问,响应是否正确,并发调用是否受限,是否存在任何其他授权问题等.

Or, is mock tests only suggested only when an API call itself is not tested (ie testing some other feature that has an API call).或者,仅在未测试 API 调用本身时才建议进行模拟测试(即测试具有 API 调用的某些其他功能)。

How do mocked tests actually test an API response if the actual HTTP call is not made?如果未进行实际的 HTTP 调用,模拟测试如何实际测试 API 响应?

They don't.他们没有。

... is mock tests only suggested only when an API call itself is not tested (ie testing some other feature that has an API call)? ... 是否仅在未测试 API 调用本身时才建议进行模拟测试(即测试具有 API 调用的其他一些功能)?

In short, YES .简而言之,的。
It is especially used in unit tests for testing specific portions of the code.它特别用于单元测试以测试代码的特定部分。

If your goal is to如果你的目标是

...test if the API is accessible, that the response is correct, concurrent calls limit, any other authorization problems, etc.. ...测试 API 是否可访问、响应是否正确、并发调用限制、任何其他授权问题等。

Then, mocking your API calls and using mocking libraries is definitely not the solution you need .然后, mocking 您的 API 调用和使用 mocking 库绝对不是您需要的解决方案 What you are looking for is somewhere in the area of End-to-End Testing , which verifies how the code/APIs work under real-world conditions, such as from an actual user perspective.您正在寻找的是端到端测试领域的某个地方,它验证代码/API 在现实条件下的工作方式,例如从实际用户的角度来看。 In that case, you need to actually call the APIs.在这种情况下,您需要实际调用 API。

The goal of tests with mocked API calls is done not to test the actual API but to test the behavior/logic of the code surrounding or dependent on the API call, such as how your code handles receiving different kinds of responses from your API or what you do with the response after successfully calling the API. It allows you to write reproducible tests that clearly defines/specifies how your code is expected to behave (related: TDD ), without needing to repeatedly call the API or relying on external dependencies (ex. setting up API auth, etc.).使用模拟 API 调用进行测试的目的不是测试实际的 API,而是测试围绕或依赖于 API 调用的代码的行为/逻辑,例如您的代码如何处理从 API 接收不同类型的响应或什么您在成功调用 API 后处理响应。它允许您编写可重现的测试,明确定义/指定代码的预期行为(相关: TDD ),而无需重复调用 API 或依赖外部依赖项(例如. 设置 API 授权等)。

Another advantage of mocking the API calls is that it allows you to repeatedly run your tests even on a local or isolated environment without a.network connection or without the proper API access credentials. mocking 和 API 调用的另一个优点是,即使在没有网络连接或没有正确的 API 访问凭证的本地或隔离环境中,它也允许您重复运行测试。 This can be especially useful for CI pipelines that might be continuously running somewhere and might not have access to your API.这对于可能在某处连续运行并且可能无法访问您的 API 的 CI 管道特别有用。

Let's take this code for example:让我们以这段代码为例:

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

It calls an API, expects a JSON response with a "message" key, and returns that "message" part of that response.它调用 API,期望带有"message"键的 JSON 响应,并返回该响应的"message"部分。 It then includes some error-handling for cases when the API did not respond as expected (to simplify, it just raises a bare Exception ).然后它包括一些错误处理,用于处理 API 未按预期响应的情况(为简化起见,它只是引发一个简单的Exception )。

The easily reproducible way to write tests for those error cases is to mock the response.为这些错误情况编写测试的易于重现的方法是模拟响应。

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)

In the above tests, you don't need to actually call the API because 1) you might not be able to consistently reproduce these error cases, and 2) you don't really need an exact attribute from the response, just whatever is in the Response object.在上面的测试中,您不需要实际调用 API,因为 1) 您可能无法始终如一地重现这些错误情况,并且 2) 您真的不需要响应中的确切属性,只要Response object。

Actually, even for the success cases, it can be useful to mock, just to define that that particular function returns whatever is set in the "message" attribute of the API response (again, related: TDD ).实际上,即使对于成功案例,模拟也是有用的,只需定义特定的 function 返回 API 响应的"message"属性中设置的任何内容(再次相关: 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!"

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM