Playwrightで権限制御をテストしていたところ、存在しないURLを開いているのにテストがPASSする偽陽性が見つかりました。
管理画面の見出しや登録ボタンが表示されないことだけを確認していたため、正しい権限拒否と、404ページなど目的とは別の画面を表示している状態を区別できなかったのです。
実際に見つかった偽陽性テストをもとに、権限制御のE2Eテストで何を確認すべきか整理します。
なぜ404ページでもテストが通るのか
修正前の構造を公開用に一般化すると、次のようなテストでした。
await page.goto('/old-client-admin');
await expect(
page.getByRole('heading', { name: '管理画面' })
).toHaveCount(0);
await expect(
page.getByRole('button', { name: '登録' })
).toHaveCount(0);
このコードが確認しているのは、管理画面の要素が存在しないことだけです。
- 権限制御によって別画面へ遷移した
- 存在しないURLを開いて404ページが表示された
- 想定外のエラーページが表示された
どの状態でも、指定した見出しとボタンがなければassertionは通ります。
さらに、Playwrightのpage.goto()は、サーバーから404や500などの有効なHTTPレスポンスが返っても、それだけでは例外を投げません。これはPlaywright公式のPage APIにも明記されています。
したがって、page.goto()が完了し、対象要素が存在しなかったというだけでは、意図した拒否経路を通った証拠にはなりません。
「入口」「遷移先」「見せない要素」を分けて確認する
修正後は、正規の開始URLへアクセスし、期待する最終URLと遷移先固有の見出しを確認したうえで、既存の否定assertionを維持しました。
以下も内部パスや固有名を置き換えた説明用の一般化例であり、実際に使用したコードそのものではありません。
await page.goto('/clients');
await expect(page).toHaveURL(/\/forbidden$/);
await expect(
page.getByRole('heading', { name: 'アクセスできません' })
).toBeVisible();
await expect(
page.getByRole('heading', { name: '管理画面' })
).toHaveCount(0);
await expect(
page.getByRole('button', { name: '登録' })
).toHaveCount(0);
確認の役割は次の四つです。
- 正規の入口へアクセスする
- 期待する最終URLへ遷移したことを確認する
- 遷移先を識別できる固有要素を肯定assertionで確認する
- 見えてはいけない管理要素を否定assertionで確認する
toHaveURL()は、現在のページが期待するURLへ遷移したことを確認するPlaywrightのWeb-first assertionです。使い方はPageAssertionsの公式資料で確認できます。
否定assertionを削除する必要はありません。問題は、それだけでテストを成立させることです。
肯定assertionは「期待する画面を表示している」ことを確認し、否定assertionは「権限のない機能を表示していない」ことを確認します。両者は似ていますが、証明している内容は別です。
HTTPステータスを直接確認すれば十分か
今回の目的は、権限のないユーザーが正しい拒否経路を通り、期待する画面へ到達することの確認です。そのため、最終URLと遷移先固有の見出しをassertionへ追加しました。
404そのものを検証するテストであれば、page.goto()の戻り値からresponse.status()を確認する方法もあります。ただし、権限制御のテストでステータスだけを確認しても、利用者が最終的にどの画面を見ているかまでは分かりません。
テストの目的に応じて、少なくとも次を分けて考える必要があります。
- HTTP応答を確認したいのか
- リダイレクト先を確認したいのか
- 表示された画面を確認したいのか
- 権限のない機能が存在しないことを確認したいのか
一つのassertionですべてを証明しようとすると、テストは短くなっても意味が曖昧になります。
修正後の検証結果
修正後は、Edge、workers=1の条件で次の結果を確認しました。
- 対象を限定したテスト:4 passed / 0 failed
- 対象spec全体:8 passed / 0 failed / 0 skipped
存在しない旧URLを正規URLへ変更し、最終URLと遷移先固有の見出しを確認することで、404ページなど意図しない画面でも通り得た判定構造を解消しました。
なお、ここで確認したのは特定の権限制御E2Eと対象specです。すべての404処理や権限制御方式を検証した結果ではありません。また、修正前の応答についてHTTPステータスを直接assertした記録はないため、旧テストで404ステータスを実測したという意味ではありません。
まとめ
「管理要素がない」という結果だけでは、権限制御が正しく動作したとは判断できません。URLを間違えていても、エラーページを表示していても、同じ結果になるからです。
権限制御のE2Eテストでは、次の役割を分けて確認します。
- 正しい入口から開始したか
- 期待するURLへ遷移したか
- 期待する画面を表示しているか
- 見えてはいけない要素が存在しないか
否定assertionは必要です。ただし、肯定assertionと組み合わせて初めて、「何もないからPASS」ではなく「意図した拒否経路を通ったからPASS」と判断できるテストになります。
関連記事
テストが通った結果を人間がどう解釈するかについては、noteの「通ったテストの外側に、仕事が残っていた。」で扱っています。