同一のプロダクト要件からUIテストとAPIテストを生成する方法

ほとんどのチームは1つの機能に対して1つのPRDを書き、テストが行われる場合でも、誰かがそれをUIテストに変換し、別の誰か(または同じ人が別の日に)がAPIテストに変換します。この2つの変換は別々に行われ、時には異なる人物が同じドキュメントを参照しながらも互いを参照しないため、しばしばずれが生じます。ここでは、代わりに単一のPRDから両方を直接生成し、構造的に同期を保つ方法を解説します。
別々の変換がずれていく理由
PRDは要件を一度だけ記述します。「ユーザーはプロフィール写真を更新できる」。UIテストを書く人はこれを読み、アップロードボタン、ファイルピッカー、プレビュー状態について考えます。APIテストを書く人は同じ文章を読み、アップロードエンドポイント、ファイルサイズ制限、ストレージのレスポンスについて考えます。どちらも合理的な解釈ですが、各レイヤーが処理すべき具体的なエッジケース、たとえばサイズオーバーのファイルに何が起きるか、APIがアップロードを拒否したときにUIが表示すべきエラー状態、について両者が合意することを強制するものは何もありません。
このずれは時間とともに拡大します。PRDに小さな更新(「ドラッグ&ドロップアップロードもサポート」)が加わると、UIテストスイートは更新されてもAPIテストスイートは更新されないということが起こりがちです。両者が別々に保守されており、両方が同じ現在の要件を反映しているかを具体的に確認する人がいないためです。
単一ソースからの生成が実際に解決すること
UIとAPIの両方のテスト生成が、2つの独立して保守された解釈からではなく、同一の正規化されたPRDから読み込む場合、要件への変更は同じ方法で両方のサーフェスに伝播します。さらに重要なのは、最初の生成が要件についての共通理解から始まるため、UIテストがAPIに期待することと、APIテストの実際の動作が、別々に形成された2つのメンタルモデルではなく、同一のソースに対して検証されることです。
実践的な手順
1. 両レイヤーを暗黙的に念頭に置いて要件を書く(またはエージェントに推測させる)。PRDにUIとAPIの別々のセクションを書く必要はありません。明確に記述された要件(「ユーザーは最大5MBのプロフィール写真をアップロードできる。サイズオーバーのファイルはアップロード開始前にインラインエラーを表示する」)は、追加の変換作業なしに、UIテスト(インラインエラー状態)とAPIテスト(サイズ検証)の両方を生成するのに十分な情報を既に含んでいます。
2. 正規化ステップが両テスト生成パスの参照元となる単一の内部PRDを構築するようにする。これが2つのレイヤーを同期し続ける構造的な要素です。UIテスト生成器とAPIテスト生成器がそれぞれ独立して元のドキュメントを解釈するのではなく、どちらも同じ正規化された内部バージョンを基に動作するべきです。
3. インタラクションと表示状態をカバーするUIテストケースを生成する。プロフィール写真の例では、アップロードインタラクション自体、成功したアップロード後のプレビュー、そしてサイズオーバーのファイルに対するインラインエラー状態が対象となり、それぞれが同一の要件にトレースされます。
4. 実際に観測された動作に基づいて、エンドポイントの実際のコントラクトをカバーするAPIテストケースを生成する。UIテストが期待することからアップロードエンドポイントが返すものを想定するのではなく、このステップでは実際のレスポンス、実際のステータスコード、実際のエラーペイロードを観測し、そこからアサーションを生成する必要があります。ここでは、UIのエラー処理動作から推測するだけでなく、サイズ制限の要件がAPIに対して直接検証されます。
5. 2つの生成されたスイートを、PRDに対してそれぞれ独立して検証するだけでなく、互いに対してもクロスチェックする。UIテストがサイズオーバーのアップロードに対して特定のエラーメッセージを期待し、APIテストが実際のレスポンスで異なる表現のエラーを観測した場合、そのミスマッチは、UIがバックエンドの実際のエラーをサイレントに表示できないという実際のバグになる前に検出する価値があります。
6. 要件が変わるたびに両方を一緒に再実行する。両方のテストスイートが同じ正規化されたPRDに基づいているため、要件を更新した場合(例えばファイルサイズの上限を10MBに引き上げるなど)、その制限を参照しているUIテストとAPIテストの両方が自動的に再生成されるか、変更が必要な箇所としてフラグが立てられます。2か所を手動で探して更新する必要はありません。
最も重要な場面
このアプローチが最も効果を発揮するのは、UIとAPIが特定の仕様で一致している必要がある機能です。具体的には、バリデーションルール、エラーメッセージ、ユーザーが実際に遭遇するエッジケースの挙動などが該当します。UIとAPIの連携が単純な部分(バックエンドのロジックをほぼ持たない静的ページなど)には、それほど重要ではありません。レイヤー間のコントラクトに実際のリスクが伴う箇所に、最大限の注意を向けることが重要です。
現在のテストがすでにズレていないかを素早く確認する方法
UIテストとAPIテストを別々に管理している場合、それらが今も同期しているとは限りません。既存のテストを過信する前に、簡単な監査を行う価値があります。両方のレイヤーに関わる要件を2〜3個選び、各テストスイートが確認しているエラーメッセージ・フィールド名・エッジケースが、PRDだけでなく互いにも一致しているかを確認してください。ある程度運用が続いたプロジェクトでは、要件変更後にUIテストだけ更新されてAPIテストが放置されていた、あるいはその逆、というケースがよく見られます。このようなズレは、今後は単一の正規化されたソースから両方を生成することで防ぐことが目的です。ただし、既存のカバレッジが完全に信頼できると思い込む前に、すでにズレが生じていないかを把握しておくことが大切です。
正式な引き継ぎプロセスがないチームでの実態
小規模なチームでは、フロントエンドとバックエンドのテストスイートを連携させるための文書化されたプロセスが存在しないことが多いです。そのようなプロセスが整備されているのは、通常、フロントエンドとバックエンドのサブチームが分かれており、連携のために明示的なコントラクトが必要な大規模企業です。小規模チームにおける非公式な対応策はシンプルで、「ある要件の実装を変更する際は、必ずUIテストとAPIテストの両方がまだ意味をなしているかを確認する」という習慣を徹底することです。共有ソースから両方を生成することで、締め切りのプレッシャーで忘れがちな部分は自動化できます。しかし、根本的な習慣、つまり「要件の変更はデフォルトで両方のレイヤーに影響する」という認識は、ツールが機械的な部分を担うようになっても引き続き大切にする価値があります。
まとめ
独立して管理された2つの解釈からではなく、同じ要件ドキュメントからUIテストとAPIテストを生成することで、フロントエンドチームとバックエンドチームが同じ機能に対して異なる理解を持つことで生まれるズレを解消できます。TestSpriteは、お客様が作成したPRDや、コードベースから直接推論されたPRDをもとに、単一の正規化されたPRDから両方のレイヤーを生成します。