APIテスト手法を、その手法だけが見つけられる問題で整理する

スモークテスト

  • 見つけられるもの:デプロイが壊れていること。

  • コスト:数分。常に実行します。これほど速く教えてくれるものは他にありません。

機能テスト

  • 見つけられるもの:不正な入力を含む、単一エンドポイントの誤った挙動。

  • コスト:中程度。他のすべてが前提とする土台です。

統合テスト

  • 見つけられるもの:各ステップは通るのに失敗する一連の流れ。

  • コスト:状態が絡むぶん高め。ユーザーに見えるバグに対する効果は最大です。

リグレッションテスト は独立した手法ではなく、変更のたびに既存のテストを実行するという頻度の問題です。 コントラクトテスト は、独立してデプロイされるサービス間で生じるデータ構造のずれを見つけます。 負荷テスト は、処理能力の限界を見つけます。 ファジング は、不正な形式の入力によるクラッシュを見つけます。 セキュリティの挙動のテスト は、認可の誤りを見つけます。しかも、一般に思われているよりはるかに低コストです。

分類を間違えられがちなもの

リグレッションテストは手法の一つとして列挙され、そのうえで作業として予定に組み込まれます。多くの場合はリリース前です。そこが間違いです。リグレッションとは、既存のテストが実行されたときに起きていることであり、その価値はほぼ実行頻度だけで決まります。月に一度しか回さないリグレッションスイートは、問題を1か月遅れで見つけるスイートです。

すべてを実施せずに選ぶ

  1. 重要なエンドポイントに対するスモークテストと機能テスト。 実用になるカバレッジとしては、最も安価なものです。

  2. 実際のユーザーの流れに対する統合テスト。 ユーザーが実際に遭遇するバグがあるのは、ここです。

  3. 認可のチェック。 低コストであり、実運用のAPIで最も多く突かれる欠陥です。

  4. コントラクトテストは、サービスが独立してデプロイされる場合にのみ。

  5. 負荷テストとファジングは、それに見合うだけのものが懸かっている場合に。 どちらも実際に意味のある手法ですが、どちらも最初に投資すべき対象ではありません。

ブラックボックスとホワイトボックス

APIのテストにおいては、かつてほど重要ではなくなった区別です。公開インターフェース越しにテストするのが既定の選択として正しいのは、クライアントが使うのがそれだからです。内部構造の知識は、何をテストするかを選ぶうえでは役立ちますが、どうテストするかには役立ちません。

APIにおいてテストピラミッドが誤解を招く点

テストピラミッドは、ユニットとユーザーインターフェースからなる世界を想定して描かれたもので、これをそのままAPIのテストに当てはめると、よくある間違いが生まれます。

ピラミッドは、上層の遅いテストは少なくせよと説きます。しかしAPIでは、どの層が高コストなのかが自明ではありません。APIテストはブラウザテストよりはるかに安価で、ユニットテストよりはるかに高価であり、そのどちらでも見つけられないものを見つけます。APIテストをピラミッドの狭い頂点に当てはめたチームはテストが少なすぎる状態になり、広い底辺に当てはめたチームは、些細なアサーションばかりの遅いスイートを抱えることになります。

サービスに対しては、ある問いにそもそも答えられるのはどの層かを考えるほうが、うまく機能する捉え方です。計算が正しいかどうかはユニットテストの領域です。エンドポイントがその計算を正しく適用しているかはAPIテストの領域です。ユーザーがそこにたどり着けるかどうかは、その両方より上の層の領域です。こうして比率は、図からではなく自分のプロダクトから決まります。

どの手法にも共通して必要な土台

APIテストのスイートが最初の1年を生き延びられるかどうかは、4つの要素で決まります。期限切れになるセッション、実行時にしか存在しない値、互いに依存する呼び出し、そして誰も片付けないレコードです。TestSpriteはこれらを、Auto-Authentication、Dynamic Variables、Dependency Chains、Auto-Cleanupとして扱います。詳細については、 APIテストのドキュメントをご覧ください。

ターミナル

npm install -g @testsprite/testsprite-cli
testsprite setup

ローカルにインストールしたくない場合は、ダッシュボードでも同じ範囲をカバーできます。コマンドの全体像については、 CLIリポジトリをご覧ください。

方法は2つあり、選択は実質的に、パイプラインを誰が持つかという話です。ダッシュボードから GitHub App を接続する方法では、リポジトリに一切変更を加える必要がありません。ビルドがすでに発行しているデプロイに反応するからです。一方、 GitHub Actions のステップを追加する方法では、チェックがリポジトリ内に置かれ、ビルドの他の部分と同じようにレビューの対象になります。

TestSpriteがカバーする手法

機能テストと統合テスト、そしてセキュリティのうち挙動に関わる部分、さらに作業ではなく頻度としてのリグレッションです。生成されるテスト計画は、機能、スキーマ、認可、エラー処理、境界値のカテゴリにわたり、平易な言葉で調整できます。

Auto-Authenticationは実行中セッションを維持し、Dynamic Variablesは呼び出し間で値を受け渡し、Dependency Chainsは各ケースが必要とするものと生成するものから実行順序を導き、Auto-Cleanupはその実行が作成したものだけを削除します。これによってリグレッションは、リリース前に予定される作業ではなく、実際に継続的なものになります。そしてこの点こそが、どの手法であれ投じたコストに見合うかどうかを最も大きく左右します。

コントラクトテスト、負荷テスト、ファジングは、別の道具のままです。利点は、効果の高い3つの手法が、テストを書く時間をめぐって機能開発と競合しなくなることです。その結果、カバレッジは計画されるだけのものではなく、実際に存在するものになります。

最も多くのバグを見つける手法はどれですか?

多くのプロダクトでは統合テストです。ユーザーに見える不具合は、1回の呼び出しの内部よりも、呼び出しと呼び出しの間で現れることが多いからです。

スモークテストは別のスイートとして用意する価値がありますか?

あります。デプロイのたびに走る2分のチェックと、20分かかるスイートとでは役割が違います。両者を一緒にしてしまうと、どちらも適切なタイミングで実行されなくなります。

ファジングは必要ですか?

不正な形式の入力がサービスに届くことで実害が生じるのであれば、必要です。多くの社内向けAPIでは、優先度は認可のチェックや統合テストのカバレッジより下になります。

探索的なAPIテストはどうですか?

確かに価値がありますが、自動化はできません。APIが新しいときや大きく変わったときに実施し、そこで見つけたことをテストとして書き起こしてください。

これらはテストピラミッドとどう対応しますか?

厳密には対応しません。ピラミッドはユニットとUIを想定して描かれたものです。APIでは、どの層に属するかよりも、そのチェックでしか見つけられないものは何かという軸のほうが役立ちます。

要点

その手法だけが見つけられるもので選びましょう。

APIテスト手法は、暗記するよりも、コストとその手法ならではの効果を比べて順位づけする価値があります。まずスモークテストと機能テスト、本当のバグを見つけるために統合テスト、低コストなので認可のチェック、そして最後に負荷テストです。