はじめに
エンジニアニメ、ありがとうございました! nikkieです。
さくらインターネットさんで櫻木真乃さんのお話をしたよ...
FastAPIのテストについての続編です。
現時点の思考のログといった趣です。
私は経験少ない(最近書き始めた)ので、「もっとよいやり方がある」という情報をお待ちしています🙏
動作環境は、はじめてTestClientの記事と同じです
目次
- はじめに
- 目次
- StreamingResponseを返すFastAPIアプリ
- HTTPXでstreaming responseをさばく
- 現時点の結論:HTTPX同様にTestClientでもstreamをさばいてテストする
- Streaming Responseとして扱わなくてもテストは書けた
- 終わりに
StreamingResponseを返すFastAPIアプリ
fastapi.responses.StreamingResponse
にイテレータを渡すだけです1。
https://fastapi.tiangolo.com/advanced/custom-response/#streamingresponse
Takes an async generator or a normal generator/iterator and streams the response body.
from fastapi import FastAPI from fastapi.responses import StreamingResponse app = FastAPI() @app.get("/stream") async def stream(): def generator(): yield b"data: Hello,\n\n" yield b"data: World!\n\n" return StreamingResponse(generator(), media_type="text/event-stream")
(本来テストを書きたいコードがasync def
なので、await
を使っていないですがasync def
に揃えました2)
このエンドポイントにcurlしてみます(fastapi dev app/main.py
でアプリケーション立ち上げ)。
% curl -N http://127.0.0.1:8000/stream data: Hello, data: World!
HTTPXでstreaming responseをさばく
FastAPIのTestClientの前に、(curlの代わりに)HTTPXからどう扱うかを考えました。
ドキュメント「Streaming Responses」
https://www.python-httpx.org/quickstart/#streaming-responses
>>> import httpx >>> client = httpx.Client() >>> with client.stream("GET", "http://127.0.0.1:8000/stream") as response: ... for line in response.iter_lines(): ... print(line) ... data: Hello, data: World!
現時点の結論:HTTPX同様にTestClientでもstreamをさばいてテストする
. └── app/ ├── __init__.py ├── main.py └── test_main.py
from fastapi.testclient import TestClient from .main import app client = TestClient(app) def test_Server_sent_events(): with client.stream("GET", "/stream") as response: assert response.status_code == 200 assert response.headers["content-type"] == "text/event-stream; charset=utf-8" assert list(response.iter_lines()) == ["data: Hello,", "", "data: World!", ""]
先ほどのHTTPXのコードを元に、クライアントをTestClient
に変更しただけです。
TestClient
はHTTPXと同じように使えます3。
Use the TestClient object the same way as you do with httpx. (Using
TestClient
)
list(response.iter_lines())
のところですが、HTTPXはいろいろな方法でStreaming Responsesを扱えるので4
assert response.read() == b"data: Hello,\n\ndata: World!\n\n"
のようにも書けました。
今回はstream呼び出し側でiter_lines()
を使うので、こちらを採用しています。
Streaming Responseとして扱わなくてもテストは書けた
採用していませんが、気付きとして残しておきます。
TestClient
でstream()
メソッドを使わなくても書けました。
def test_Server_sent_events(): response = client.get("/stream") assert response.status_code == 200 assert response.headers["content-type"] == "text/event-stream; charset=utf-8" assert response.text == "data: Hello,\n\ndata: World!\n\n"
単にget()
メソッドだと、httpx.Response
が返ります。
https://www.python-httpx.org/api/#response
text
属性はcurl
でいうバッファのような動きなのですね(stream()
のときはcurl
のunbufferぽいですね)
FastAPIアプリを使うコードに合わせて、stream()
メソッドで呼び出すテストコードを今回採用しました(この記事で意見ほしいポイントです)
終わりに
StreamingResponseを返すFastAPIアプリのテストを、stream()
メソッドで考えました。
今回重視していたのは、FastAPIを使うコードに合わせてテストを書くことです。
TestClient
はHTTPXと同じインターフェースで使え、streamの扱いも豊富というのが1つ学びです。