テストケース
テスト戦略
| テスト種別 | 対象 | ツール | 実行場所 |
|---|---|---|---|
| API テスト | backend の route の振る舞い | bun test |
heineken-survey-design-backend |
| 単体テスト | frontend のドメインロジック(分岐評価・バリデーション) | Vitest | heineken-survey-design-frontend |
| 単体テスト | 拡張の CS 変換ロジック | Vitest | Lab-web-extension |
| 統合テスト | dev 環境の画面操作・プレビュー走行 | Playwright | integration-tests |
| 手動テスト | Creative Survey への流し込みと実機回答 | 拡張の API ログ + 目視 | CS 編集画面 |
テスト実行方法
# backend の API テスト
cd heineken-survey-design-backend
bun test
bun test src/app.test.ts # 単一ファイル
bun test -t "テスト名" # 名前フィルタ
# frontend の単体テスト
cd heineken-survey-design-frontend
bun run test
bun run test -- lib/surveys/preview-runner.test.ts
# 拡張の単体テスト
cd Lab-web-extension
pnpm test
# dev 環境に対する統合テスト
cd integration-tests
bun install
bun run auth # 初回のみ。dev に Google ログインしてセッションを保存
bun run test # 01 → 02 を通しで実行
bunx playwright test --grep "R13" # 特定のルートだけ
bun run report # 直近の実行結果を HTML で確認
bun run cleanup # テストで作った案件を削除
backend の API テスト方針
createApp() に fake store と fake auth resolver を注入 し、DB を使わずに route の振る舞いを検証します。API テストで振る舞いを先に固定してから repository 実装に落とす進め方です。
const app = createApp({ store: fakeStore, resolveUser: async () => fakeUser });
const res = await app.request("/api/surveys");
テストヘルパーは src/test/helpers.ts にあります。PostgreSQL を伴う検証が必要な場合だけ、別途 integration test を追加します。
統合テスト(integration-tests)
dev 環境(survey-design-web.heineken.dev.4digit.ai)に対して、cs-import-test-cases.md のテストケースを Playwright で自動実行します。
| スペック | 内容 |
|---|---|
00-smoke.spec.ts |
dev への疎通確認(案件作成 → 設問追加 → 後片付け)。落ちたらログイン切れか環境側の問題 |
01-build-survey.spec.ts |
「作成手順」を UI 操作で再現し、CP1〜CP13 を検証 |
02-preview-run.spec.ts |
走行表 R1〜R28 をシステム側プレビューで走らせて期待遷移を検証 |
01 は describe.serial で S1 → S2 → S3 の順に流れます。作成した案件の id は .artifacts/survey-ref.json に残り、02 と 01 の再実行はこれを参照します(02 だけ流したいときは 01 を先に通しておく)。
テストシナリオの全体像
1 本の案件を決まった手順で作成し、CS へ流し込み、チェックリスト照合と回答走行を行う複合シナリオ方式です。
| セクション | 設問 | タイプ | 分岐 / 表示ロジック |
|---|---|---|---|
| S1 スクリーニング | T1 | 説明文 | — |
| Q1 性別 | SA | 回答しない → スクリーンアウト | |
| Q2 居住地 | PD | その他 → スクリーンアウト | |
| Q3 利用サービス | MA | ①排他設定(自動生成)②「だけである」→ SX | |
| S2 演算子と表示 | Q4 色(回答数 最小1・最大2) | MA | 3 ルール(AND / only / has_other)+ 評価順 |
| Q5 施策 | MA | not_includes / not_only + 表示ロジック | |
| Q6 満足度(行 × 列) | TM | セル条件 AND → SX / セル条件 → SC + 表示ロジック(行) | |
| Q7 感想(良い点 / 悪い点) | FA | 欄指定の文字列一致 → SX / いずれかの回答 → SO / 回答あり → SO | |
| S3 生活習慣 | Q8 朝食 | SA | — |
| Q9 食事(サブ設問 朝/昼/夜) | SA+サブ設問 | サブ設問「昼」条件 → SX / 表示ロジック | |
| Q10 総合満足 | SA | その他制御(Q11 が自動作成) | |
| Q11 その他 FA | FA | Q10=その他 AND 未記入 → 留まる(自動生成) | |
| Q12 年収(個人 / 世帯) | PD 複数欄 | 個人年収 1000万以上 → SX / OR ルール → SX | |
| Q13 利用チャネル(行 × 列) | TM 複数選択 | 排他設定(行ごとに自動生成) |
テストケース一覧
作成手順のチェックポイント(CP)
01-build-survey.spec.ts で検証します。
| ID | シナリオ | 期待結果 | 優先度 |
|---|---|---|---|
| CP1 | Q3 を追加する | コードが自動で Q3 になる(Q1 の重複にならない) |
高 |
| CP2 | S2 を追加する | 自動生成される設問のコードが T1-2 / Q1-2 など既存と重複しない形に振り直される |
高 |
| CP3 | 設問を削除してから追加する | エラーにならず、コードが Q4 から振られる(サーベイ全体の連番) |
高 |
| CP4 | セクションを複製する | 複製先の設問コードが T1-2 / Q1-2 などに振り直される |
高 |
| CP5 | 複製したセクション内の排他設定を見る | 分岐先が複製後の自分のコードを指している | 高 |
| CP6 | Q3 に排他選択肢を追加して保存する | 「排他選択肢を含む AND だけではない → この設問に留まる + メッセージ」が自動生成される | 高 |
| CP7 | Q10 に「その他」を設定して保存する | フォローアップ FA(Q11)が自動作成され、「Q10 でその他 AND Q11 が未記入 → 留まる」が自動生成される | 高 |
| CP8 | 回答数設定を操作する | UI は MA 選択時のみ表示。保存後も保持される。最小 > 最大は保存時エラー | 中 |
| CP9 | Q12 を PD 複数欄で設定する | 編集 UI が「項目名称 + 改行区切り textarea」。分岐画面で欄ごとにグループ表示される | 中 |
| CP10 | Q13 を TM 複数選択で設定する | 行数分(2 件)の排他ルールが自動生成される。単一選択に戻すと自動ルールが消える | 中 |
| CP11 | Q3 の排他選択肢のラベルを変更する | 自動ルールの条件とメッセージが新ラベルに追従し、手動ルールは書き換わらない | 中 |
| CP12 | Q10 の「その他」選択肢のラベルを変更する | 自動生成されたフォローアップのルールが追従する | 中 |
| CP13 | Q13 の行名をリネームする | その行の自動ルールの条件 2 行が新しい行名に追従する | 中 |
プレビュー走行(R)
02-preview-run.spec.ts で検証します。基本回答セット(Q1=男性, Q2=東京都, Q3=A, Q4=赤, Q5=施策1, Q6=A満足/B普通, Q7=空欄, Q8=はい, Q9=すべて自宅, Q10=満足, Q12=両欄とも400万未満, Q13=両行とも平日のみ)との差分だけを記載します。
| ID | 差分 | 期待結果 | 検証対象 |
|---|---|---|---|
| R1 | なし | Q4=赤 で Q5 の施策2/3 が非表示 → Q6 ルール2で Q8 へジャンプ → 完走してコンプリート | 表示ロジック, SC 変換, 終了ステップ |
| R2 | Q1=回答しない | 即スクリーンアウト。完全回答者にならない | equals, SX |
| R3 | Q2=その他 | 即スクリーンアウト | プルダウン条件 |
| R4 | Q3=[A, どれも利用していない] | メッセージが出て Q3 に留まる → 選び直すと進める | 排他設定(AND + self + message) |
| R5 | Q3=どれも利用していない のみ | スクリーンアウト | only |
| R6 | Q4=[赤,青] | スクリーンアウト(ルール3も該当しうるがルール1が先勝ち) | AND, 評価順 |
| R7 | Q4=緑 | Q7 へ(Q5, Q6 スキップ) | only, 設問ジャンプ |
| R8 | Q4=[赤,緑] | Q6 へ。さらに Q6 の行「サービスB」が非表示になる | has_other, 表示ロジック(行) |
| R9 | Q4=青 | Q5 で施策2/3 が表示される → Q5=施策2 で Q7 へ | 表示ロジック不成立, not_includes |
| R10 | Q4=青, Q5=未回答 | Q7 へ(未回答でも「を含まない」成立 = CS 準拠仕様) | not_includes × 未回答 |
| R11 | Q4=青, Q5=[施策1,施策2] | スクリーンアウト | not_only |
| R12 | Q6=A不満/B不満 | スクリーンアウト | マトリクスセル × AND |
| R13 | Q6=A普通/B普通, Q7 悪い点に「解約したい」 | スクリーンアウト(部分一致で発火するか) | FA 欄指定・部分一致 |
| R14 | 同上, Q7 良い点に「解約」 | ルール1は発火しない(欄の取り違えチェック)→ ルール3で調査完了 | FA 欄解決, answered |
| R15 | 同上, Q7 良い点に「最高です」 | 調査完了(コンプリート扱い) | FA「いずれかの回答」, SO |
| R16 | Q9 昼=食べない | スクリーンアウト | サブ設問単位の条件 |
| R17 | Q9 朝=食べない(昼夜は自宅) | 発火しない → 完走 | 分割先の取り違えチェック |
| R18 | Q8=いいえ | Q9 の朝だけ「自宅」が消える(昼・夜には残る) | 分割先ターゲットの表示ロジック |
| R19 | Q10=その他, Q11=未記入 | メッセージが出て留まる → 記入すると完走 | その他制御(未記入判定) |
| R20 | Q10=満足, Q11=未記入 | そのまま完走(その他未選択なら FA 空欄で通過) | その他制御の AND 条件 |
| R21 | Q4=[赤,青,緑] | エラーで Q4 に留まる(最大2超過。分岐より検証が先) | 回答数制限(range) |
| R22 | Q12 個人年収=1000万以上 | スクリーンアウト | PD 複数欄の条件 |
| R23 | Q12 世帯年収=1000万以上(個人は400万未満) | 発火しない → 完走(欄の取り違えチェック) | PD 複数欄の対象欄解決 |
| R24 | Q13 店舗=[平日, 利用していない] | メッセージが出て Q13 に留まる → 選び直すと進める | TM 排他(行内の同時選択) |
| R25 | Q13 店舗=[利用していない] のみ, アプリ=[平日] | 発火せず完走(別の行の選択では発火しない) | TM 排他の行単位判定 |
| R26 | Q6=A普通/B普通, Q7=空欄 | 「回答あり」が発火せず Q8 へ自然遷移 → 完走 | answered × 未入力 |
| R27 | Q12 個人年収=400万〜1000万未満 | スクリーンアウト(OR の1つ目が成立) | OR の分解 |
| R28 | Q12 世帯年収=400万〜1000万未満(個人は400万未満) | スクリーンアウト(OR の2つ目が成立) | OR の分解 |
CS 流し込み後の確認(I)
Chrome 拡張と Creative Survey 実機が必要なため Playwright では回しません。ただし拡張が出す API ログを読んで自動照合できます。
ログの取り方:
- 拡張の「CS API ログ」パネルで クリア
- 流し込みを実行
- 編集画面をリロードし、設問一覧を最後までスクロール(
is_connectは並び替え後の再取得で初めて入る。ページングされるため全ページ読み込む必要がある) - ダウンロード して
bun run check:cs <パス>
| ID | 確認内容 | 優先度 |
|---|---|---|
| I-1 | warnings が空 | 高 |
| I-2 | 並び順が 開始ステップ → T1〜Q13 → コンプリート → スクリーンアウト。終了ステップ 2 枚にラベルが自動設定されている | 高 |
| I-3 | ページ結合: Q9 の3分割 + Q10 + Q11 が同一ページ、Q8 は別ページ(is_connect は前方向) |
高 |
| I-4 | Q2 プルダウンの選択肢が 3 件に分かれている | 高 |
| I-5 | Q3 の分岐: ルール1が条件2行(AND)で宛先が自設問 + メッセージ、ルール2が「だけである」 | 高 |
| I-6 | Q4 の分岐が 3 件、この順で並んでいる(order_index) | 高 |
| I-7 | Q5 の表示制御: ターゲットに施策2・施策3 の 2 件とも入っている | 高 |
| I-8 | Q6 の条件が 行 × 列 のセル指定 | 高 |
| I-9 | Q7 の条件: ルール1の対象が「悪い点」欄、ルール2が「いずれかの回答」 | 高 |
| I-10 | Q6 ルール2の宛先が Q8(セクション完了 → 次セクション先頭への変換) | 高 |
| I-11 | Q9 の分岐が分割後の最後(夜)に付き、条件が 2 番目(昼)を参照している | 高 |
| I-12 | Q9 の表示制御が分割後の 1 番目(朝)に付き、ターゲットが「自宅」 | 高 |
| I-13 | Q11 が Q9〜Q10 と同一ページに結合され、Q11 宛ての分岐に条件 2 行が入っている | 中 |
| I-14 | Q4 の回答数制限が反映されている(is_range: true, range_min: 1, range_max: 2) |
中 |
| I-15 | Q12 が 1 設問のまま answer_items 2 件。条件対象が 1 つ目(個人年収)を指している | 中 |
| I-16 | Q13 の分岐が 2 件(行ごと)、各ルールがセル条件 2 行(AND) で宛先が自設問 + メッセージ | 中 |
| I-17 | 必須設定が FA / PD / TM で CS 側にも反映されている | 中 |
| I-18 | Q6 の表示制御: 条件が Q4=緑、非表示対象が行「サービスB」 | 中 |
| I-19 | Q7 の分岐が 3 件。3 件目が verb 3 + 検索文字列 空 + 対象「いずれかの回答」 | 中 |
| I-20 | Q12 の分岐が logic 3 件(ルール2 の OR が 2 件に分解されている) | 中 |
統合テストの注意点
- dev は他メンバーと共有している環境。テストは新しい案件を毎回作って残す運用なので、溜まってきたら
bun run cleanupで消す - API 認証は
x-user-emailヘッダ方式。既定はconfig.tsのアカウント - 画面操作は
headless: false(playwright.config.ts)。CI で回すならここを変える - 分岐の保存は画面が持っている設問データを丸ごと送り直す実装のため、自動生成ルール(排他設定など)の反映を待たずに分岐パネルを開くと、そのルールを消してしまう。
BranchPanel.openFor()が毎回リロードしているのはこのため - 保存の完了はボタンの表示で判断しない。「保存中...」が出る前に消えたと判定してしまい、直後の reload で保存リクエストがキャンセルされる。
lib/wait.tsのwaitForQuestionWrite()で POST / PATCH のレスポンスを待ってから次の操作に進むこと
落ちたときの調査先
cs-import-test-cases.md の「診断表」に、症状から修正先を引くための対応表があります。主なものは次のとおりです。
| 症状 | 想定原因 | 調査先 |
|---|---|---|
| CP1〜CP5 が NG | コード振り直しの不備 | backend survey-store.ts(buildUniqueQuestionCode / remapCopiedSectionCodes) |
| CP10 / CP11〜CP13 が NG | 自動ルールの生成・同期 | frontend auto-branch-rules.ts |
| I-6 / R6 で先勝ちしない | logic の order_index 制御 | 拡張 execute-import-plan.ts の import ボディ |
| R10 が NG(未回答で発火しない) | not_includes の未回答挙動 |
frontend preview-runner.ts |
| R13 が NG(部分一致で発火しない) | CS の FA マッチ仕様 | frontend preview-runner.ts の evaluateFreeTextCondition |
| I-11 / R16〜R17 が NG | サブ設問 index の解決 | 拡張 execute-import-plan.ts の resolveConditionTarget |
| R2 で完全回答者になる | コンプリート/スクリーンアウトの並び順 | 拡張 execute-import-plan.ts の setQuestionOrder |
| R4 が NG(差し戻されない) | 自設問宛て / message の送出 | 拡張 execute-import-plan.ts の resolveDestinationId |
| I-20 / R27・R28 が NG | OR の logic 分解 | 拡張 build-import-plan.ts の conditionGroups |
未検証の項目
CS 実機での確認が済んでいない項目です。
- 拡張の visibility 追加 POST(2 件目以降)
- プルダウンの placeholder 区切り文字
- FA の部分一致挙動
- セル条件での verb 4(だけではない)の CS 解釈
- サバイバル形式の
answer_typeと条件表現(対象外だが未収集)