如何测试使用图像的 FastAPI api 端点?

Posted

技术标签:

【中文标题】如何测试使用图像的 FastAPI api 端点?【英文标题】:How to test a FastAPI api endpoint that consumes images? 【发布时间】:2020-07-02 03:00:40 【问题描述】:

我正在使用 pytest 测试一个 FastAPI 端点,该端点以二进制格式输入图像

@app.post("/analyse")
async def analyse(file: bytes = File(...)):

    image = Image.open(io.BytesIO(file)).convert("RGB")
    stats = process_image(image)
    return stats

启动服务器后,我可以通过使用requests 运行调用来成功手动测试端点

import requests
from requests_toolbelt.multipart.encoder import MultipartEncoder

url = "http://127.0.0.1:8000/analyse"

filename = "./example.jpg"
m = MultipartEncoder(
        fields='file': ('filename', open(filename, 'rb'), 'image/jpeg')
    )
r = requests.post(url, data=m, headers='Content-Type': m.content_type, timeout = 8000)
assert r.status_code == 200

但是,在表单的函数中设置测试:

from fastapi.testclient import TestClient
from requests_toolbelt.multipart.encoder import MultipartEncoder
from app.server import app

client = TestClient(app)

def test_image_analysis():

    filename = "example.jpg"

    m = MultipartEncoder(
        fields='file': ('filename', open(filename, 'rb'), 'image/jpeg')
        )

    response = client.post("/analyse",
                           data=m,
                           headers="Content-Type": "multipart/form-data"
                           )

    assert response.status_code == 200

当使用python -m pytest 运行测试时,这给了我一个

>       assert response.status_code == 200
E       assert 400 == 200
E        +  where 400 = <Response [400]>.status_code

tests\test_server.py:22: AssertionError
-------------------------------------------------------- Captured log call --------------------------------------------------------- 
ERROR    fastapi:routing.py:133 Error getting request body: can't concat NoneType to bytes
===================================================== short test summary info ====================================================== 
FAILED tests/test_server.py::test_image_analysis - assert 400 == 200

我做错了什么? 使用图像文件编写测试函数test_image_analysis() 的正确方法是什么?

【问题讨论】:

【参考方案1】:

您会看到不同的行为,因为 requestsTestClient 在各个方面并不完全相同,因为 TestClient 包装 requests。要深入挖掘,请参考源代码:(FastAPI 使用的是来自 starlette 库的TestClient,仅供参考)

https://github.com/encode/starlette/blob/master/starlette/testclient.py

要解决,你可以去掉MultipartEncoder,因为requests 可以接受文件字节并以form-data 格式对其进行编码,类似于

# change it
r = requests.post(url, data=m, headers='Content-Type': m.content_type, timeout = 8000)

# to 
r = requests.post(url, files="file": ("filename", open(filename, "rb"), "image/jpeg"))

并修改 FastAPI 测试代码:

# change
response = client.post("/analyse",
                       data=m,
                       headers="Content-Type": "multipart/form-data"
                       )
# to
response = client.post(
    "/analyse", files="file": ("filename", open(filename, "rb"), "image/jpeg")
)

【讨论】:

请注意,如果需要,可以通过接受 python 字典的data= 包含其他表单数据。不需要多部分编码器。

以上是关于如何测试使用图像的 FastAPI api 端点?的主要内容,如果未能解决你的问题,请参考以下文章

如何在单独的文件中使用 FastAPI Depends 的端点/路由?

如何在 fastAPI 中为 <input type="file"> 定义一个 REST 端点(并使其与 Angular 一起使用)?

用Tensorflow和FastAPI构建图像分类API

使用 curl 将文件上传到 FastAPI 端点 - 307 临时重定向

如何在不使用 ImageView 的情况下从 API 端点下载图像或位图?

如何使用 FastAPI 返回 HTMLResponse