1. はじめに
アンケート管理サービスは、利用者がオンラインでアンケートに回答し、担当職員や管理者がその結果を確認・管理するサービスです。一見すると単純ですが、利用者・職員・管理者など役割ごとに権限や画面遷移が異なり、会員登録の有無によって認証方式も複数に分かれています。さらに、外部連携システムとのデータ同期まで関係しているため、個々の機能が正常に動作するだけでは、サービス全体の品質を保証することが難しい構造でした。
本プロジェクトではQAを担当し、要件定義書を基に統合テストシナリオを自ら作成し、それを開発(dev)、ステージング(stg)、本番(prod)の3段階の環境で実行・管理する業務を行いました。その結果、最終的に71個のテストシナリオ(TS)と376個の詳細テストケース(TC)で構成された統合テストドキュメントを作成し、運用しました。
この記事では、単に「テストを実施した」という結果報告にとどまらず、なぜこのような構成でシナリオを設計したのか、どのような基準で例外ケースを導き出したのか、そしてドキュメントをどのように管理すれば長期にわたって信頼できる資産になるのかを中心に、経験を整理します。
2. 統合テストシナリオの設計が必要だった背景
プロジェクト初期には、機能単位の単体テストによって各APIと画面の正常動作を検証していました。しかし、会員登録から本人認証、規約への同意、病院情報の連携まで続くフローのように、複数の画面とAPIが順番に連動する機能では、各段階が個別には正常でも、全体のフローで状態値が正しく伝達されなかったり、前の段階の例外処理が次の段階に影響したりする問題が実際に存在しました。
また、利用者・職員・医療従事者・管理者など、役割によって同じ画面でも表示される情報や実行可能な操作が異なっていました。そのため、「この役割がこの画面にアクセスしたとき、意図したとおりに制限されるか」といった検証は、機能単位のテストでは代替できませんでした。そこで、要件定義書に記載された個々の機能をユーザーフロー単位に再構成した統合テストシナリオが必要だと判断しました。
シナリオを別途設計した実務上の目的は3つありました。リリース前の回帰テスト範囲を明確に定義すること、新しく参加したメンバーがドキュメントだけでサービス全体の流れを理解できるようにすること、そしてバグが発生した際に、どのシナリオとケースで再現するのかを追跡するための基準点を設けることです。
3. 要件定義書をテストシナリオとして構造化する方法
要件定義書は通常、機能単位で整理されています。これをそのままテスト項目に移すと、1つのケースが過度に大きくなったり、逆に細かく分割されすぎて全体の流れが見えなくなったりする問題が生じます。これを解決するため、シナリオドキュメントを2段階の構造で設計しました。
第1段階はテストシナリオ(TS、Test Scenario)です。TSとは、利用者が1つの目的を達成するためにたどる業務フロー全体を、1つの単位としてまとめたものです。たとえば「規約への同意および本人認証」という1つのシナリオは、氏名の入力、携帯電話認証、認証番号の確認という複数の段階を含む、1つの目的指向のフローとして定義します。
第2段階はテストケース(TC、Test Case)です。TCとは、1つのシナリオ内で検証すべき個別の条件を意味します。先ほどの「規約への同意および本人認証」シナリオであれば、氏名が未入力の場合の処理、形式が正しくない携帯電話番号を入力した場合の処理、認証番号が一致しない場合の処理など、複数のケースが下位に存在することになります。
このようにシナリオとケースを分離した構造は、実務上大きなメリットをもたらしました。シナリオ一覧だけを確認すればサービス全体の業務フローを一目で把握でき、実行時にはケース単位で進捗を細かく管理できます。また、バグが発生した際には、どのシナリオのどのケースなのかをすぐに特定できるため、開発チームとのコミュニケーションコストも大幅に削減できました。
4. 2つの軸で分類する:対象と機能領域
シナリオとケースの数が増えるほど、単純な一覧形式のドキュメントでは管理が難しくなります。71個のシナリオと376個のケースを効率的に管理するため、すべてのケースに2つの分類軸を付与しました。
第1の軸は「対象」です。利用者、職員、医療従事者、管理者、キオスクなど、実際にその機能を使用する役割を基準に分類しました。同じ機能であっても、役割によって画面構成やアクセス権限の動作が異なるため、この軸を分けなければ、特定の役割でのみ発生する問題を見落としやすくなります。
第2の軸は「区分」で、認証、帳票、アンケート、統計可視化、マイページ、コミュニケーションボックス、ダッシュボード、メニュー、管理など、サービスの機能領域を基準に分類しました。この軸は、開発チームが特定領域のコードを修正した際に、その領域に属するケースだけを選んで回帰テストを実施できるようにする目的で設計しました。
この2つの軸を掛け合わせることで、「管理者画面の認証関連機能だけを再検証する」や「利用者が使用するすべての機能を網羅的に検証する」といった形で、ドキュメント全体を確認しなくても必要な範囲だけをすぐに抽出できました。リリースサイクルが短く、毎回376ケースすべてを再実行するのは難しかったため、この分類体系は回帰テストの範囲を合理的に絞り込むうえで実質的に貢献しました。
5. Happy Pathを超えた例外ケースの導出
要件定義書には通常、正常なフロー、すなわちHappy Pathが記載されています。しかし、実際のサービス運用中に発生する問題の多くは、正常なフローではなく、その周辺の例外状況で発生します。要件を文字どおり移すだけでは、こうした例外ケースを漏れなく導き出すことが難しいため、シナリオを作成するたびに、次の4つの検証パターンを繰り返し適用しました。
-
必須値検証:入力が必要な項目を空欄にしたまま、次のステップを試行する場合
-
形式検証:定められた形式(桁数、使用可能な文字など)から外れた値を入力する場合
-
状態ベース検証:すでに存在する、または重複している状態、あるいは値同士が一致しない場合
-
ポリシー検証:制限時間の超過、試行回数の制限など、ポリシーによってフローが変わる場合
以下の表は、実際にこの4つのパターンを認証関連シナリオに適用した例をまとめたものです。
|
検証パターン |
例示条件 |
テスト項目 |
期待結果 |
|---|---|---|---|
|
必須値検証 |
氏名未入力 |
氏名フィールドを空欄にした状態で次のステップへの進行を試みる |
氏名の入力を促すメッセージが表示され、次のステップへ進めない |
|
形式検証 |
携帯電話番号の桁数エラー |
形式が正しくない携帯電話番号を入力して認証番号を要求する |
形式エラーメッセージが表示される |
|
状態ベース検証 |
IDの重複 |
すでに使用中のIDで重複確認を要求する |
重複を知らせるメッセージが表示される |
|
ポリシー検証 |
認証番号の有効時間超過 |
認証番号の送信後、制限時間内に入力せず待機する |
時間切れのメッセージが表示され、再送信が可能になる |
たとえば、要件定義書には単に「携帯電話の認証番号を入力して認証を処理する」と1行で記載されていました。しかし、上記のパターンを当てはめると、実際には認証番号の未入力、形式エラー、不一致、有効時間の超過、試行回数の超過など、5つ前後のケースに派生しました。このように、要件ドキュメントに明示されていないケースをQAの観点から先行して導き出す作業が、シナリオ作成の中で最も多くの時間と注意を要した部分でした。
6. dev、stg、prod環境ごとのテスト戦略
作成したシナリオは、開発(dev)、ステージング(stg)、本番(prod)の3段階の環境を通して実行しました。3つの環境はそれぞれ目的が異なるため、同じケースであっても、環境に応じて異なる観点から検証しました。
dev環境では、管理されたテストデータを用いて、機能が要件どおりに実装されているか、つまりロジック自体の整合性を迅速かつ繰り返し検証しました。stg環境では、本番に近い条件で外部連携システムとのデータ同期や権限体系を再検証しました。外部システムとの処理順序や応答遅延に関する問題の多くは、この段階で明らかになりました。
prod環境では、リリース直後に主要フローと今回のリリースで変更された領域だけを確認するスモークテストを行い、検証範囲を最小限に抑えました。このように環境ごとに目的と範囲を変えて設定したことで、同じシナリオドキュメントを再利用しながらも、テストリソースを効率的に配分できました。
7. テスト結果を記録する原則
テストを進めていると、すべてのケースが成功または失敗に明確に分けられないことがよくあります。外部システムの対応が先行して初めて検証できる場合や、ポリシー上、検証対象から除外すべき場合も実際に数多く存在しました。このような状況を単純に失敗として記録すると、後からこの記録を参照する人が問題の原因を誤解する可能性があるため、結果を次の4種類に分けて記録する原則を定めました。
|
結果区分 |
意味 |
記録原則 |
|---|---|---|
|
PASS |
期待どおりに正常動作することを確認した |
特記事項なしで結果のみを記録 |
|
FAIL |
期待結果とは異なる動作をすることを確認した |
備考欄に再現手順を記載し、該当するTC_IDを引用したバグレポートと関連付けて管理 |
|
ABORTED |
環境またはポリシー上の理由により、そのケースを実施しない、または実施できない |
備考欄に実施できなかった具体的な理由を必ず記載 |
|
BLOCKED |
外部システムまたは他組織の対応が先行して初めて検証できる |
備考欄に、どのような条件が満たされれば再検証できるかを記載 |
特にABORTEDとBLOCKEDとして記録したケースには、必ず備考欄に具体的な理由を残しました。例えば、特定の通知機能については、外部システムの変更通知の受信と照会APIの呼び出しが1つのトランザクションとして処理される場合、正確な結果を保証できないことが確認されたため、ロジックを分離した後に再検証することにしました。これを単純に「失敗」として残すのではなくBLOCKEDに分類し、原因と再検証の条件を併せて記録しておいたことで、後にロジックが分離された時点で、該当するケースだけを正確に見つけて再実行できました。
これと併せて、顧客企業のシステム画面がサービス内に埋め込まれて提供される領域のように、検証と修正の主体がプロジェクト内部にない機能も存在しました。このような領域でエラーが見つかった場合、自社で直接修正することはできないため、その画面の担当者を特定し、再現条件とともに修正を依頼しました。そして反映を確認した後、該当するケースだけを再実行して結果を更新する方法で対応しました。FAILとして記録されたケースも、その多くを担当開発者に引き渡し、修正版のデプロイ後に同じTC_IDで再検証する流れを繰り返して解消していきました。この過程で、先に整理したID体系と結果分類の原則が、組織間コミュニケーションの基準点として有効に機能することを確認できました。
8. シナリオ文書の識別子(ID)管理原則
ケース数が増えるほど、各ケースを識別するID体系が文書全体の信頼性を左右することを、実務を通じて実感しました。シナリオにはTS_IDを、ケースにはTC_IDを順番に付与しましたが、これらのIDはシナリオ文書内だけで使用されるものではなく、バグレポート、回帰テスト結果表、デプロイチェックリストなど、複数の成果物で繰り返し参照されました。
この過程で守った最も重要な原則は、「一度付与したIDは、検証しようとする条件そのものが変わらない限り、内容を上書きしない」ということでした。特定のケースの検証目的が完全に変わる場合は、既存IDの内容を修正するのではなく、既存IDを廃止扱いとし、新しいIDを発行して別のケースとして追加する方法を採用しました。誤字の修正や表現の調整程度の軽微な変更であれば同じIDを維持したまま修正しましたが、検証条件や期待結果そのものが変わる場合は、例外なく新しいIDを付与しました。
この原則を守った理由は、IDが単なる番号ではなく、特定時点のテスト履歴を指し示す参照点だからです。同じIDの下で内容を完全に別のケースへ変更してしまうと、過去にそのIDを引用して残したバグレポートや実行履歴が、実際には別の内容を指すことになり、混乱が生じます。これは、データベーススキーマの変更やAPIのバージョン管理において、既存バージョンをそのまま残したうえで新しいバージョンを追加する方法と同じ原理だと言えます。さらに、一度廃止したIDはその後も再利用しないことを併せて原則とし、文書の履歴を時系列に沿って完全に保存できるようにしました。
9. 適用結果と振り返り
以上の原則に基づき、71のシナリオと376のテストケースで構成された統合テスト文書を完成させ、dev、stg、prodの3環境で順次実行しました。大半のケースは正常に合格し、一部は外部システム連携や環境上の制約によりABORTEDまたはBLOCKEDに分類され、別途管理する対象として残りました。
最も大きな学びは、統合テストのシナリオ作成において本当に多くの時間を要するのは、要件定義書を読むことではなく、文書に明記されていない例外状況を見つけ出すことだという点です。4つの検証パターンを機械的に当てはめる習慣や、ID管理・結果分類のように一見些細に見えるルールが、ケース数が数百に増えた時点から文書全体の信頼性を支えることも、実務を通じて確認しました。
今後、同程度の規模のプロジェクトを担当することになった場合は、今回確立したシナリオ設計の原則と文書管理のルールを、プロジェクトの初期段階からチーム全体で共有できる形に文書化したいと考えています。また、繰り返し実行される回帰テストのうち自動化可能な領域を選別し、手動テストのリソースを、例外ケースの発見のように人の判断が重要な領域へ集中させるのがよいと思います。
eunice