API結合テストに明示的なグラフが必要な理由

実行順序が偶然に左右されるテストランナーがたまたま選んだ順序では成功しても、別のマシンでは失敗します。誰かが順序制御のプラグインを追加し、問題は恒久化します。
1件の失敗が20件に見える作成処理が失敗すると、その下流のすべてが失敗し、画面が赤一色になります。最初の本当の失敗を探し出すこと自体が仕事になってしまいます。
並列実行が安全に行えない何が何に依存しているかが分からなければ、安全に同時実行することはできません。その結果、テストスイートは直列のまま遅い状態が続きます。

依存関係を明示する

解決策は、各テストが「何を必要とし、何を生成するか」を宣言することです。それさえあれば、ランナーは安全な実行順序を導き出し、独立したブランチは並列に実行され、失敗が起きたときはそれに依存するテストが失敗ではなくスキップとして記録されます。20行の赤は、1件の失敗と19件のスキップに変わります。これはデバッグ体験としてまったくの別物です。

これが、1つのルートを個別に検証するEndpoint Testsと、あるレスポンスが次のステップの入力になる複数ステップの連鎖であるIntegration Testsとの違いです。

値の受け渡しという問題

作成処理は、後続のステップが必要とする識別子を返します。それをハードコードすれば、テストが動くのは一度きりです。実行時に取得すれば、チェーンはデータを正しく次へ受け渡します。文字にすると当たり前に見えますが、先月まで成功していたスイートが今日失敗する原因として最も多いのがこれです。

チェーンにおけるクリーンアップ

結合テストは、エンドポイント単体のテストよりも多くの状態を作り出します。しかもそれは依存関係のある順序で作られるため、後処理もその順序を踏まえなければなりません。注文より先に顧客を削除することはできません。同時実行に耐えられるのは、その実行が作成したものだけを依存関係の逆順で片付けるクリーンアップだけです。

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

チェーンが4番目のステップで失敗したときにすべきこと

依存関係を宣言しておくことの価値は、失敗がどう見えるかに最もはっきり表れます。その結果を読み解く順序を知っておくと役に立ちます。

まず、失敗がそのステップ自体にあるのか、入力にあるのかを確認します。誤った識別子を受け取ったステップが失敗したのは、上流の何かが誤った値を生成したからであり、そのステップを直しても症状を直したことにしかなりません。

次に、クリーンな状態から実行しても失敗するかを確認します。一度目は成功し、再実行すると失敗するチェーンは、クリーンアップが行われていないことを示しています。これは今追っているバグとは別の問題であり、多くの場合そちらのほうが重要です。

そして最後に、エンドポイント自体を見ます。重要な情報はたいてい最初の2つの確認に含まれています。だからこそ、赤い行がフラットに並んだ一覧は、1件の失敗と19件のスキップというグラフに比べて、デバッグのコストがはるかに高くつくのです。

チェーンは短く保つ

12ステップのチェーンが9番目で失敗すると、原因の切り分けは難しく、再実行にも時間がかかります。1つのユーザーの意図をカバーする3〜5ステップが、長く使い続けられるサイズです。チェーンが長い場合、たいていは2つのジャーニーが1つに混ざっています。

始め方

ターミナル

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

何もインストールしたくない場合は、ダッシュボードでも同じことができます。コマンドラインでできるそのほかの操作については、 CLIリポジトリ。

ダッシュボードからリポジトリを接続すれば、すでに作成しているデプロイをきっかけにテストが実行されます。あるいは、自前のワークフローにステップを追加する方法もあります。

TestSpriteはどのようにグラフを明示するのか

各テストケースは、自分が必要とするものと生成するものを宣言し、Dependency Chainsがそこから安全な実行順序を導き出します。独立したブランチは並列に実行され、何かが失敗した場合、それに依存するテストは失敗ではなくスキップとして記録されます。その結果、20行の赤は1件の失敗と19件のスキップになります。

値の受け渡しはDynamic Variablesが担うため、チェーンは誰かが貼り付けた数値ではなく、作成呼び出しが返した識別子を次へ運びます。Auto-Cleanupは、各実行が作成したものを依存関係の逆順で削除します。これが同時実行を安全にします。

その効果は、チェーンが最初に壊れたときに表れます。画面いっぱいの赤を読み進める代わりに、どこで食い違ったのかを指し示す1件の失敗が得られます。これが、5分で原因を特定できるか、午後いっぱいを費やすかの違いです。

エンドツーエンドテストとは何が違うのですか?

エンドツーエンドテストは通常、インターフェース経由でのテストを指します。API結合テストは同じジャーニーをサービス層で実行するため、より高速で、どこで失敗したかをより正確に特定できます。

結合テストでは実際の依存先を使うべきですか?

結合テストであれば、そのとおりです。それこそが目的です。連携相手をモックにしてしまうと、別の問いに答えていることになります。

ステップ間のテストデータはどう扱えばよいですか?

各ステップが生成したものを取得し、次へ渡します。共有のシードデータが便利なのは、2つのチェーンが同じレコードに触れるまでの話です。

チェーンに含まれるサードパーティのサービスはどうすればよいですか?

サンドボックスが提供されていれば、それを使います。提供されていない場合は、そのステップを分離し、チェーンにはそこが境界になると割り切ります。

結合テストはどのくらいの数が必要ですか?

エンドポイントの組み合わせごとにではなく、重要なユーザージャーニーごとに1つです。組み合わせは無限にありますが、ジャーニーはそうではありません。

まとめ

グラフを宣言するか、永遠にデバッグし続けるかです。

API結合テストは一連の流れを対象とし、その流れには依存グラフがあります。各テストが必要とするものと生成するものを宣言すれば、安全な実行順序、並列実行、そして読み取りやすい失敗が手に入ります。値はハードコードせずに実行時に取得し、チェーンは短く保ちましょう。