4
2

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 1 year has passed since last update.

【FastAPI】APIキーを利用したPOSTリクエストのpytestテスト方法

Posted at

概要

FastAPIアプリケーションの特定のエンドポイントの動作を検証するテストコードを実装しました。
本記事では、GETリクエスト、APIキーを使用したPOSTリクエストのテストケースを紹介します。

以前、Djangoで実装した際の記事も作成したので、Djangoで実装する方はこちらご参考ください。
【Django】APIキーを利用したPOSTリクエストをテストするためのテストケースの作成方法

サンプルコード解説

GETリクエストのテスト

main.py
from fastapi import FastAPI

app = FastAPI()
## 以下省略
test_main.py
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.pytest_main.pyが同ディレクトリにある前提です。
そうでない場合は、import文のfrom .main import appを変えてください。
これにより、main.pyモジュールのappオブジェクトをインポートしています。

client = TestClient(app)は、FastAPIアプリケーションをテストするためのTestClientオブジェクトを作成しています。このテストクライアントを利用して、getpostなどのHTTPメソッドを送信します。

test_get_success()関数では、client.get()メソッドを使用して/apiエンドポイントにGETリクエストを送信し、レスポンスのステータスコードが200であることを確認しています。
test_から始まる名前の関数を作成するのは、pytestの命名規則と同じですね。

実行時はpytestで可能です。
pytestは事前にインストールしてください。pytestとはテスト関数を自動的に検出し、実行するためのテストランナーのことです

POSTリクエストのテスト

次にリクエストヘッダーとリクエストボディを投げるパターンのテストコードを実装します。

test_main.py
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'}}

その場合、以下のようになります。

test_main.py

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ヘッダーを設定する必要があります。ちょっと躓いたのでメモ。

参考

テスト - FastAPI

4
2
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
4
2

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?