APIリクエストがレスポンスを返すだけでは、テストに合格したことにはなりません。それは単なるレスポンスです。テストは、そのレスポンスが正しいことを何らかの形で確認したときにのみ存在します。その確認がアサーションであり、アサーションの品質が、テストスイートが実際のバグを検出できるか、単にサーバーが稼働していることを確認するだけにとどまるかを決定します。
このガイドでは、APIアサーションとは何か、作成する価値のあるタイプ、チームが間違いを犯しやすい点、そしてスクリプトを使わずにApidogでアサーションを視覚的に構築する方法について説明します。
APIアサーションとは
アサーションとは、テストに合格するために真でなければならないレスポンスに関する記述です。リクエストを送信するとAPIが応答し、アサーションはその応答の一部を期待される値と比較します。一致すれば合格、一致しなければ失敗です。
アサーションがなければ、自動テストはエンドポイントに到達可能であることを証明するだけです。アサーションがあれば、エンドポイントが正しいことを証明します。この2つのギャップに、ほとんどの運用上のインシデントが存在します。APIは稼働しており、200を返したが、ボディが間違っていた、といったケースです。
役立つアサーションは、具体的で独立している必要があります。具体的であるとは、失敗が一つのことを指し示すこと。独立しているとは、他のアサーションが先に合格することに暗黙的に依存しないことです。単一のテストステップには通常、複数のアサーションが含まれており、それぞれが同じレスポンスの異なる側面をチェックします。
ステータスコードアサーションと、それが十分ではない理由
最も一般的なアサーションはHTTPステータスコードをチェックします。成功した読み取りには200、リソース作成には201、不正な入力には400、認証不足には401を期待します。これは必要不可欠です。ステータスコードを正しく扱うことはそれ自体が一つの規律であり、もしあなたのAPIが一貫性がないのであれば、REST APIが使用すべきHTTPステータスコードは一読の価値があります。
しかし、ステータスコードアサーションだけでは不十分です。APIは、空のボディ、古い値、オブジェクトがあるべき場所にnull、あるいは成功を装ったエラーメッセージとともに200 OKを返すことがあります。ステータスはリクエストが処理されたことを示すだけで、データが正しいかどうかについては何も語りません。
ステータスアサーションはテストステップの最初の一行として扱い、決して唯一の一行としないようにしてください。
作成する価値のあるアサーションのタイプ
ボディ内容アサーション。 レスポンス内の実際の値をチェックします。`id`フィールドが存在し、空ではないこと。`email`が送信したものと一致すること。`total`が明細項目の合計と等しいこと。これらはステータスコードでは見落とされがちなロジックのバグを検出します。
スキーマアサーション。 JSONスキーマやOpenAPI定義に対してレスポンスの形状を検証します。必須フィールドが存在すること、型が正しいこと、予期しないフィールドが出現していないこと。スキーマアサーションは、バックエンドが密かにフィールドを文字列からオブジェクトに変更し、すべてのクライアントを壊すような契約のずれを検出します。これは、生産者と消費者間でアイデアを形式化するAPI契約テストと重複します。
ヘッダーアサーション。 `Content-Type`が`application/json`であること、キャッシュヘッダーが意図通りに設定されていること、CORSヘッダーが存在すること、`Strict-Transport-Security`のようなセキュリティヘッダーが存在することを確認します。
応答時間アサーション。 レイテンシー予算(例えば800ミリ秒)を設定し、応答が遅い場合にテストを失敗させます。パフォーマンスの回帰は他のすべてのアサーションタイプでは見えないため、これは機能スイートでそれらを捕捉する唯一のタイプです。
エラー形状アサーション。 ネガティブケースでは、4xxコードだけでなくエラーボディもアサートします。`error`フィールドが`validation_error`と等しいこと、`details`配列が問題のあるフィールド名を指定していること、そしてメッセージに機密情報が漏洩していないこと。
セキュリティアサーション。 エンドポイントがトークンなしのリクエストを拒否すること、期限切れのトークンを拒否すること、ユーザー間の認可を強制すること、および注入ペイロードをエスケープせずに返すことがないことを確認します。
ステータス、2つか3つのボディフィールド、スキーマ、および応答時間予算をアサートするテストステップは、真の仕事を行っています。ステータスのみをアサートするステップは、装飾的なものです。
アサーションロジックがうまくいかないケース
揮発性データへの過剰なアサーション。 正確な`created_at`タイムスタンプや生成されたUUIDをアサートすると、テストが無意味に毎回失敗します。フィールドが存在し、正しい型であることをアサートし、正確な値をアサートしないようにしましょう。
ハッピーパスでのアサーション不足。 ハッピーパスのテストは、ステータスコードのみをアサートする可能性が最も高いものです。これはユーザーが最も利用するパスでもあります。最も少ないアサーションではなく、最も徹底したアサーションを適用しましょう。
順序依存のアサーション。 アサーションBがアサーションAが合格した場合にのみ意味をなすのに、両方が盲目的に実行される場合、Aの失敗はBに混乱を招く二次的な失敗を引き起こします。依存関係が明示的になるようにステップを構成しましょう。
1つのアサーションで2つの仕事をさせる。 「レスポンスが正しい」はアサーションではありません。それを分割しましょう。ステータスは200、tokenが存在する、expires_inは3600と等しい。3つのチェック、3つの明確な失敗メッセージです。
ネガティブケースの無視。 チームは成功ケースには熱心にアサートしますが、失敗ケースにはほとんどアサートしません。ボディアサーションがないネガティブケースは、APIが「ノー」と言ったことだけを証明し、それが「正しく」ノーと言ったことは証明しません。
Apidogでのアサーションの構築
Apidogでは、アサーションは視覚的なテストビルダーの一部であるため、スクリプトを書くのではなく、クリックで定義できます。
テストシナリオ内の任意のリクエストに対して、アサーションパネルを開いてチェックを追加します。
- ステータスアサーション。 「response status(レスポンスステータス)」を選択し、
200と等しくなるように設定します。 - ボディフィールドアサーション。
$.tokenのようなJSONPath式を使用して、それが存在し、空でない文字列であることをアサートします。$.expires_inが3600と等しいことをアサートします。Apidogはレスポンス構造を読み取るため、パスを盲目的に入力する代わりにフィールドを選択できます。 - スキーマアサーション。 エンドポイントの定義済みスキーマに対してレスポンスを検証します。ApidogはAPIデザインとテストを1つのワークスペースに保持するため、アサーションが使用するスキーマはドキュメントが公開するスキーマと同じであり、乖離する2つ目のコピーは存在しません。
- 応答時間アサーション。 応答時間が予算を下回ることをチェックする項目を追加します。
- カスタムスクリプト(必要な場合のみ)。 視覚的なチェックでは表現できないロジックには、JavaScriptのポストプロセッサーを使用します。ほとんどのアサーションにはこれは不要です。
シナリオ全体でアサーションをグループ化すると、Apidogはそれらをまとめて実行します。拡張性のあるカバレッジのために、データファイルを添付して、単一のアサーションセットがデータ駆動型テスト入力のすべての行に対して実行されるようにします。シナリオが実行されると、生成されたレポートには、どのリクエストでどのAssertionが失敗したか、期待値と実際の値が並べて正確に表示されます。
同じシナリオはCIで変更なく実行されるため、すべてのコミットで各アサーションが再チェックされます。CI/CDでのAPIテストの自動化でその連携について説明しています。Apidogをダウンロードして、ご自身のエンドポイントに対してアサーションセットを構築してみてください。
アサーションセットの実例
ユーザーオブジェクトを返すGET /users/{id}を例にとります。ハッピーパスの確実なアサーションセットは次のとおりです。
- ステータスは
200であること Content-Typeヘッダーにapplication/jsonが含まれていること$.idがリクエストされたIDと等しいこと$.emailが存在し、メールパターンに一致すること$.roleがadmin、member、viewerのいずれかであること- レスポンスボディが
Userスキーマに準拠していること - 応答時間が600ミリ秒未満であること
そして、不明なIDを持つGET /users/{id}の場合:
- ステータスは
404であること $.errorがnot_foundと等しいこと- レスポンスボディに
email、id、またはその他のユーザーフィールドが含まれていないこと
2つのリクエスト、11のアサーションで、契約、データ、ヘッダー、パフォーマンス、およびエラーの動作を検証しました。これこそが、リリースを保護するテストスイートと、単にサーバーにPingを打つだけのテストスイートとを分けるものです。
CIパイプラインにおけるアサーション
アサーションは自動的に実行されたときにその真価を発揮します。誰かが週に一度手動で実行するスイートは、バグを1週間遅れて検出します。CIに組み込まれた同じスイートは、プルリクエストの段階でバグを検出します。
パイプラインでアサーションを実行する際には、2つの設計上の選択が重要です。第一に、失敗が明確でなければなりません。「テスト失敗」とだけ表示されるCIログは、開発者にローカルでの再現を強います。「POST /auth/loginで$.expires_inが3600になることを期待したが、7200だった」と表示されるログは、開発者にすぐにどこを調べるべきかを伝えます。強力で具体的なアサーションこそが、そのような分かりやすい失敗をもたらします。
第二に、アサーションは環境間で安定している必要があります。本番環境のユーザーIDをハードコードするアサーションは、コードとは関係のない理由でステージング環境で失敗します。環境固有の値は変数に保持し、正確な値が環境に依存する場所では構造と型をアサートします。スキーマアサーションは環境間をうまく移動しますが、特定の生成されたIDに対するアサーションはそうではありません。
実用的なパターンとしては、すべてのエンドポイントでステータスとスキーマをベースラインとしてどこでも実行し、その上に環境を考慮した値のアサーションを重ねます。ベースラインはどの環境での契約のずれも検出し、値アサーションは安定したデータがある場合のロジックのバグを検出します。両方をすべてのコミットで実行することで、テストスイートは単なるレポートではなく、ゲートとしての役割を果たすようになります。
よくある質問
アサーションとテストケースの違いは何ですか? テストケースは、リクエストとその期待される結果を含む全体のチェックです。アサーションは、その中で合否を決定する個々の比較です。APIテストケースの書き方を参照してください。
1つのリクエストにいくつアサーションを持つべきですか? ステータス、主要なボディフィールド、スキーマ、およびレイテンシー予算をカバーするのに十分な数です。ほとんどのエンドポイントでは4〜8個です。それぞれが異なることをチェックしていれば、それ以上でも問題ありません。
正確なレスポンスボディをアサートすべきですか? 安定したフィールドは正確に、揮発性フィールドは型または存在によってアサートします。タイムスタンプや生成されたIDを含む完全なボディをアサートすると、毎回失敗するテストが作成されます。
機能テストでAPIのパフォーマンスをアサートできますか? はい。応答時間アサーションは、通常の機能実行中にレイテンシーの回帰を捕捉します。基本的な予算チェックには、別途負荷テストは必要ありません。
ネガティブテストケースにもアサーションは必要ですか? 絶対に必要です。そして、これらは最もアサーションが手薄になりがちなケースです。ステータスコードのチェックしかないネガティブケースは、APIが「ノー」と言ったことだけを証明し、それが正しくノーと言ったことは証明しません。エラーフィールド、問題のあるフィールドの詳細、メッセージ中の機密データの不在をアサートしてください。
カスタムアサーションスクリプトはどこで使うべきですか? スクリプトは、視覚的なビルダーでは表現できないロジックのために取っておいてください。例えば、リクエスト間の比較、派生値、以前のレスポンスに依存する条件付きチェックなどです。ほとんどのアサーション、つまりステータス、スキーマ、ボディフィールド、タイミングに関するものは、視覚的なチェックとしてよりクリーンでレビューしやすいです。
