概要
FastAPIアプリケーションの特定のエンドポイントの動作を検証するテストコードを実装しました。
本記事では、GETリクエスト、APIキーを使用したPOSTリクエストのテストケースを紹介します。
以前、Djangoで実装した際の記事も作成したので、Djangoで実装する方はこちらご参考ください。
【Django】APIキーを利用したPOSTリクエストをテストするためのテストケースの作成方法
サンプルコード解説
GETリクエストのテスト
from fastapi import FastAPI
app = FastAPI()
## 以下省略
from fastapi.testclient import TestClient
from .main import app
client = TestClient(app)
def test_get_success():
response = client.get("/api")
assert response.status_code == 200 # ステータスコードが200 OKであることを確認
main.py
とtest_main.py
が同ディレクトリにある前提です。
そうでない場合は、import文のfrom .main import app
を変えてください。
これにより、main.py
モジュールのapp
オブジェクトをインポートしています。
client = TestClient(app)
は、FastAPIアプリケーションをテストするためのTestClient
オブジェクトを作成しています。このテストクライアントを利用して、get
やpost
などのHTTPメソッドを送信します。
test_get_success()
関数では、client.get()
メソッドを使用して/api
エンドポイントにGETリクエストを送信し、レスポンスのステータスコードが200であることを確認しています。
test_
から始まる名前の関数を作成するのは、pytest
の命名規則と同じですね。
実行時はpytest
で可能です。
※pytest
は事前にインストールしてください。pytest
とはテスト関数を自動的に検出し、実行するためのテストランナーのことです
POSTリクエストのテスト
次にリクエストヘッダーとリクエストボディを投げるパターンのテストコードを実装します。
from fastapi.testclient import TestClient
from .main import app
import copy
client = TestClient(app)
def test_get_success():
response = client.get("/api")
assert response.status_code == 200
## 追加 ##
# テスト用のリクエストデータを作成
params = {
"sample_id": "",
"xyz_cd" : "",
"information": {
"name": "abcdef",
"address": "12345"
}
}
def test_post_with_api_key():
x_api_key = "SAMPLE_API_KEY"
request_data = params.copy()
response = client.post("/api", json=request_data,
headers={"Content-Type": "application/json", "x-api-key": x_api_key})
assert response.status_code == 200
def test_post_with_wrong_api_key():
x_api_key = "WRONG_API_KEY"
request_data = params.copy()
response = client.post("/api", json=request_data,
headers={"Content-Type": "application/json", "x-api-key": x_api_key})
assert response.status_code == 401
test_post_with_api_key()
関数ではSAMPLE_API_KEY
という正しいAPIキーが入っているのでステータスコード200でテストに成功します。ちなみに、json
引数を使用することで、request_data
オブジェクトがJSON形式のデータとしてリクエストに含まれることが保証されます(ただし、JSONにシリアライズできるdict
である必要あり)。JSONの代わりにフォームデータを送信する必要がある場合は、代わりにdata
パラメータを使用するとのこと。
test_post_without_api_key()
関数は、WRONG_API_KEY
という誤ったAPIキーを提供してPOSTリクエストを送信しているので、ステータスコード401(Unauthorized)でテストに成功します。
任意のエラーステータスとエラーメッセージを返すようにしている場合は都度ここを変える必要あります。
エラーレスポンスでテストする場合
例えば、status
で期待通りのレスポンスを返す場合は0000
、期待通りのエラーレスポンスを返す場合は2222
だとします。また、レスポンスのJSONは、response
エントリの中にstatus
があるとします。
レスポンス形式:
{'response': {'sample_id': 'xxx', 'status': '0000', 'msg': 'xxxxxx'}}
その場合、以下のようになります。
def test_post_with_api_key():
x_api_key = "SAMPLE_API_KEY"
request_data = params.copy()
response = client.post("/api", json=request_data,
headers={"Content-Type": "application/json", "x-api-key": x_api_key})
response_data = response.json()
assert response.status_code == 200
assert response_data["response"]["status"] == "0000"
def test_post_with_wrong_api_key():
x_api_key = "WRONG_API_KEY"
request_data = params.copy()
response = client.post("/api", json=request_data,
headers={"Content-Type": "application/json", "x-api-key": x_api_key})
response_data = response.json()
assert response.status_code == 200
assert response_data["response"]["status"] == "2222"
補足1: print
デバッグしたいときは?
レスポンスの中身がどうなっているか確認したい場合などありますよね。
しかし、通常のprint
文を使っても、pytest
の実行時にはターミナルに出力されません。
そこで、pytest -s
コマンドを実行することで、標準出力を有効にすることができます。
ただし、本番環境でテストを実行する場合は外した方が良いですね。
補足2:content_type
引数について
ちなみに、DjangoのTestCase
では、以下のように記載します。
from django.test import TestCase
class ApiTestCase(TestCase):
def test_post_sample_with_api_key(self):
headers = {'HTTP_X_API_KEY': 'SAMPLE_API_KEY'}
data = {'key': 'value'}
response = self.client.post('/api/', data=data, follow=True, content_type='application/json', accept='application/json', **headers)
self.assertEqual(response.status_code, 200)
def test_sample_without_api_key(self):
headers = {'HTTP_X_API_KEY': 'WRONG_API_KEY'}
data = {'key': 'value'}
response = self.client.post('/api/', data=data, follow=True, content_type='application/json', accept='application/json', **headers)
self.assertEqual(response.status_code, 401)
HTTPX
の使い方をベースとしているとのことだったのでこれと同じ形でいけると思ったら、TestClient
オブジェクトでは、通常content_type
引数を受け取るようなメソッドは提供されていない模様。以下のエラーが出ます。
TypeError: TestClient.request() got an unexpected keyword argument 'content_type'
TestClient
メソッドを適切に利用してリクエストを送信する場合、headers
引数を使用して、Content-Type
ヘッダーを設定する必要があります。ちょっと躓いたのでメモ。