本文へ移動

ホーム / QA / 第3部 テストの自動化

第3部 テストの自動化・第8章

E2E テストの設計と安定化

★★★ 必須8トピック・面接 5問

ブラウザを動かす E2E テストを、遅く不安定にしないための設計を押さえる。例は Playwright で書くが、考え方は他の道具でも同じ。

ひとことで

Playwright などで操作を再現する。待ち方・テストデータ・独立性で不安定さを防ぐ。

E2E(End to End)テストは、ブラウザやアプリを実際に操作して、利用者の流れを通しで確かめるテストです。Web では Playwright や Cypress、Selenium などを使います。最も利用者に近い確かめ方ですが、遅くて不安定になりやすいので、設計の工夫が欠かせません。

利用者の操作ブラウザフロントエンドAPIDBすべて本物をつないで、通しで確かめる(だから遅く、壊れやすい)
E2E テストが確かめる範囲
道具特徴向いている場面
PlaywrightChromium・Firefox・WebKit を動かせる。自動待機・並列・トレースが標準新しく始める Web の E2E
Cypressブラウザの中で動き、画面を見ながら書きやすいフロントエンドの開発者と一緒に書く
Selenium歴史が長く、多くの言語とブラウザに対応既存の資産がある、Java などで書きたい

E2E で確かめるのは、ログイン・検索・購入のような主要な流れに絞ります。入力のパターンの違いなど細かい確認は、API や単体のテストに任せます。

面接ではこう答える

E2E テストは、ブラウザやアプリを実際に操作して、フロントエンドから API、DB までをつないだ状態で、利用者の流れを通しで確かめるテストです。最も利用者に近い確かめ方ですが、遅くて壊れやすいので、ログインや購入のような主要な流れに絞ります。Web では Playwright や Cypress がよく使われ、待ち方とテストデータの設計で安定させます。

8.2Page Object パターン

用語★★★ 必須
ひとことで

画面ごとに要素の場所と操作をまとめたクラスを作り、テスト本体は確かめることだけを書く設計。画面の変更は1か所の修正で済む。

Page Object:テストと画面の間に「画面の部品」の層を置くテスト本体何を確かめるかだけPage Object画面ごとの要素と操作画面(ブラウザ)ボタン・入力欄login()getByRole など画面が変わったら、直すのは Page Object の1か所だけテスト本体(たくさんある)はそのまま使える
Page Object を使った E2E テストの層

Page Object は、画面ごとに「どこに何があるか」と「できる操作」をまとめたクラスを作る設計です。テスト本体は「何を確かめるか」だけを書き、画面が変わったときは Page Object の1か所を直せば済みます。

// pages/login.ts
export class LoginPage {
  constructor(private page: Page) {}
  async login(email: string, password: string) {
    await this.page.getByLabel('メールアドレス').fill(email);
    await this.page.getByLabel('パスワード').fill(password);
    await this.page.getByRole('button', { name: 'ログイン' }).click();
  }
}

// tests/login.spec.ts
test('正しいパスワードでログインできる', async ({ page }) => {
  await page.goto('/login');
  await new LoginPage(page).login('qa1@example.com', 'correct-pass');
  await expect(page.getByRole('heading', { name: 'マイページ' })).toBeVisible();
});

Page Object には「確認」を書かず、操作と要素の場所だけを持たせるのが一般的です。確認はテスト本体に書くと、何を確かめているかが読みやすくなります。

面接ではこう答える

Page Object パターンは、画面ごとに「どこに何があるか」と「できる操作」をまとめたクラスを作る設計です。テスト本体は、ログインする、購入する、のような操作を呼んで確かめることだけを書きます。画面の作りが変わっても Page Object の1か所を直せば済むので、テストが増えても保守しやすくなります。

ひとことで

E2E で画面の要素を探す方法。CSS のクラス名より、役割と名前・ラベル・テスト用の目印で探すと壊れにくい。

要素は、見た目や構造で変わりやすい CSS のクラス名より、役割と名前(getByRole・getByLabel)や、テスト用の目印(data-testid)で探すと壊れにくくなります。

探し方例(Playwright)壊れにくさ
役割と名前getByRole('button', { name: '購入' })高い。利用者に見えるものと同じ
ラベルgetByLabel('メールアドレス')高い。入力欄に向く
表示されている文字getByText('保存しました')中。文言の変更で壊れる
テスト用の目印getByTestId('cart-total')高い。ただし開発者に付けてもらう
CSS・XPathlocator('div.sc-x9 > span:nth-child(2)')低い。見た目の変更ですぐ壊れる

役割と名前で探せないときは、アクセシビリティの問題(ボタンに名前がない)であることも多いです。テストを書きながら、そうした問題も報告できます。

面接ではこう答える

ロケーターは、E2E テストで画面の要素を探す指定のことです。CSS のクラス名や XPath は見た目の変更ですぐ壊れるので、ボタンの役割と名前、入力欄のラベル、テスト用の data-testid のような、利用者に見えるものや変わりにくい目印で探します。役割と名前で探せない要素は、アクセシビリティの問題であることも多いです。

ひとことで

固定の時間を待つ sleep ではなく、表示や通信の完了など条件がそろうまで上限つきで待つ。Playwright は操作と確認で自動で待つ。

E2E が不安定になる一番の原因は待ち方です。表示や通信が終わる前に操作・確認すると、速い日は通り、遅い日は落ちます。固定の時間を待つ sleep ではなく、条件がそろうまで待つようにします。

固定の sleep(3 秒)実際の表示 1 秒3 秒待つ:速い日はむだ、遅い日(4 秒)は失敗条件を待つ(表示されるまで、上限つき)表示 1 秒表示された瞬間に次へ。遅い日も上限まで待てる
固定の sleep と、条件を待つやり方の違い
// 悪い例:3 秒待ってから確かめる
await page.waitForTimeout(3000);
expect(await page.textContent('.msg')).toBe('保存しました');

// 良い例:表示されるまで再試行しながら待つ
await expect(page.getByText('保存しました')).toBeVisible();

Playwright は、クリックなどの操作の前に、要素が表示され操作できる状態になるまで自動で待ちます(自動待機)。expect の確認も、条件を満たすまで一定時間くり返し試します。この仕組みに任せて、自分で sleep を書かないのが基本です。

面接ではこう答える

E2E テストでは、固定の時間を待つ sleep を使うと、速い日はむだに待ち、遅い日は失敗します。そこで、要素が表示されるまで、通信が終わるまで、のように条件がそろうまで上限つきで待ちます。Playwright では、操作の前に要素が操作できる状態になるまで自動で待ち、expect の確認も条件を満たすまで再試行するので、この仕組みに任せるのが基本です。

ひとことで

コードを変えていないのに通ったり落ちたりするテスト。待ち方・データの共有・環境・テスト自体の作りが主な原因。

見つける再実行で通った一覧隔離する一時的に外す・期限原因を分類待ち方・データ・環境直して戻すトレースで確かめる
flaky なテストへの対処の流れ

コードを変えていないのに、通ったり落ちたりするテストを flaky(フレーキー)と呼びます。放っておくと「どうせまた落ちただけ」と本物の不具合まで見逃すので、原因を分類して直します。

原因例対策
待ち方表示や通信の完了前に操作・確認している固定の sleep をやめ、要素の状態や通信の完了を待つ(Playwright の自動待機・expect の再試行を使う)
データ他のテストとデータを共有していて、順番で結果が変わるテストごとに専用のデータを作る。前提のデータを API で用意する
環境検証環境が重い・外部サービスが不安定・時刻や乱数に依存外部はモックにする。時刻を固定する。環境の状態を監視する
テスト自体壊れやすい要素の指定、テスト同士の順番への依存役割で要素を探す。テストを独立させ、順番を入れ替えても通るか確かめる

失敗したときのスクリーンショット・動画・トレース(Playwright の Trace Viewer)を残すと原因を探しやすくなります。自動の再実行(Playwright の retries。既定は 0 回)は CI だけで1〜2回までにとどめることが多く、「再実行で通ったテスト」は一覧にして直す対象にします。直せないものは一時的に外し(隔離)、期限を決めて直します。

面接ではこう答える

flaky テストは、コードを変えていないのに、通ったり落ちたりするテストです。放っておくと「また落ちただけ」と本物の不具合まで見逃すようになります。原因は主に、待ち方、テスト同士のデータの共有、検証環境や外部サービス、壊れやすい要素の指定の4つです。再実行で通ったテストを一覧にし、直せないものは期限を決めて隔離し、トレースで原因を調べて直します。

すべてのテストで画面からログインすると、時間がかかり、ログイン画面の不具合で全部のテストが落ちます。ログインは最初に1回だけ行い、その状態を使い回します。

ログインの準備最初に1回だけログイン状態Cookie などをファイルにテスト 1テスト 2テスト 3各テストは保存した状態から始めるので、画面からのログインを毎回しない
ログイン状態の使い回し(Playwright の storageState の考え方)

Playwright では、準備用のプロジェクトでログインし、Cookie などの状態(storageState)をファイルに保存して、各テストはそのファイルを読み込んで始められます。ログイン画面そのもののテストは、別に数本だけ書きます。

前提を画面で作る代わりに使うもの
毎回ログイン保存したログイン状態
画面から商品を登録データ作成の API・シード
権限の違うユーザーを画面で作る役割ごとの専用アカウントと、その状態のファイル

確かめたいこと以外は画面を通さない、が E2E を速く安定させるこつです。

「サーバーが 500 を返したときの表示」「0 件のときの表示」を本物の環境で作るのは大変です。E2E の道具の機能で、ブラウザの通信を途中で差し替えて偽の応答を返すと、簡単に再現できます。

ブラウザテストする画面通信の差し替えroute本物の API偽の応答500・空・遅い
通信を差し替えて、エラーや空の状態を作る
test('注文の API が失敗したらエラーを表示する', async ({ page }) => {
  await page.route('**/api/orders', (route) =>
    route.fulfill({ status: 500, body: '{}' }));
  await page.goto('/cart');
  await page.getByRole('button', { name: '注文する' }).click();
  await expect(page.getByText('時間をおいて再度お試しください')).toBeVisible();
});

エラー・空・遅い応答など、画面の異常系の表示をまとめて確かめられます。ただし、差し替えたテストは本物の API の不具合は見つけられないので、主要な流れは本物とつないだ E2E でも確かめます。

ひとことで

画面のスクリーンショットを基準の画像と比べて、見た目が意図せず変わっていないかを確かめるテスト。

ビジュアル回帰テストは、画面のスクリーンショットを前回の基準の画像と比べ、見た目が意図せず変わっていないかを確かめるテストです。CSS の変更で別の画面が崩れた、のような、機能のテストでは見つからない不具合に向きます。

基準の画像前回の正しい見た目今回の画像テストで撮る差分違う所に色を付ける差があれば落ちる → 意図した変更なら基準を更新、意図しないなら不具合
ビジュアル回帰テスト(スクリーンショットの比較)の仕組み

Playwright では await expect(page).toHaveScreenshot(); で比べられ、意図した変更のときは --update-snapshots を付けて流すと基準の画像を更新できます。

不安定になる原因対策
日時・広告・アニメーションなど毎回変わる部分その部分を隠す(マスクする)、アニメーションを止める
OS やフォントの違いで描画がわずかに違う基準を撮る環境と比べる環境をそろえる(CI のコンテナで撮る)
わずかなずれで落ちる許す差の大きさ(しきい値)を設定する

面接ではこう答える

ビジュアル回帰テストは、画面のスクリーンショットを前回の基準の画像と比べ、違いがあれば知らせるテストです。CSS の変更で別の画面のレイアウトが崩れた、のような、機能のテストでは見つけにくい不具合に向きます。日時や広告のように毎回変わる部分を隠し、撮る環境をそろえ、許す差の大きさを決めると安定します。

この章のまとめ

🖨 まとめシート(印刷・PDF)
  • E2E は主要な流れに絞り、Page Object で保守しやすくする
  • 壊れにくいロケーターと、条件を待つ待ち方
  • flaky を見つけて隔離し、原因を分類して直す
  • E2E テストとは・道具の選び方:Playwright などで操作を再現する。待ち方・テストデータ・独立性で不安定さを防ぐ。
  • Page Object パターン:画面ごとに要素の場所と操作をまとめたクラスを作り、テスト本体は確かめることだけを書く設計。画面の変更は1か所の修正で済む。
  • 要素の探し方(ロケーター):E2E で画面の要素を探す方法。CSS のクラス名より、役割と名前・ラベル・テスト用の目印で探すと壊れにくい。
  • 待ち方(固定の sleep をやめる):固定の時間を待つ sleep ではなく、表示や通信の完了など条件がそろうまで上限つきで待つ。Playwright は操作と確認で自動で待つ。
  • flaky テストの原因と対処:コードを変えていないのに通ったり落ちたりするテスト。待ち方・データの共有・環境・テスト自体の作りが主な原因。
  • ビジュアル回帰テスト(見た目の比較):画面のスクリーンショットを基準の画像と比べて、見た目が意図せず変わっていないかを確かめるテスト。

この章のクイズ

面接ではこう聞かれる

まず答えを見ずに声に出して答え、そのあと開いて比べる。

Q1E2E テストが不安定なとき、どう直しますか?

模範回答

まず失敗の記録(スクリーンショット・トレース・ログ)を見て、原因を分類します。多いのは「待ち方」「データ」「環境」の3つです。

待ち方の問題は、表示や通信が終わる前に操作しているもので、固定の sleep をやめて、要素が表示される・通信が終わるといった状態を待つように直します。データの問題は、テスト同士が同じデータを使っていて順番で結果が変わるもので、テストごとに専用のデータを API で作るようにします。環境の問題は、検証環境の重さや外部サービスの不安定さなので、外部はモックにし、時刻や乱数は固定します。要素の指定が壊れやすい場合は、役割やラベル、テスト用の目印で探すように変えます。

直すまでの間は、そのテストを隔離して期限つきで直す一覧に入れ、再実行(retry)で隠し続けないようにします。また、再実行で通ったテストの件数を記録して、flaky が増えていないかを見続けます。

深掘りされたら

  • retry を設定するのはどう考えますか?
  • ローカルでは通るのに CI だけ落ちるときは何を疑いますか?

見られているポイント原因の分類(待ち・データ・環境)と対策

Q2Page Object パターンとは何ですか? 使う利点は?

模範回答

E2E テストで、画面ごとに「どこに何があるか」と「できる操作」を1つのクラスにまとめる設計です。テスト本体は loginPage.login(email, password) のように操作を呼ぶだけにし、ボタンや入力欄の探し方は Page Object の中に閉じ込めます。

利点は、画面の見た目や構造が変わっても、直すのが Page Object の1か所で済み、たくさんあるテスト本体はそのまま使えることです。テスト本体も「何を確かめるか」だけになって読みやすくなります。注意点は、Page Object の中に確認(assert)を入れすぎないことと、要素は CSS のクラス名より、役割と名前(getByRole)やテスト用の目印(data-testid)で探して壊れにくくすることです。

深掘りされたら

  • 画面の一部(ヘッダーなど)が多くの画面で共通なときは、どう作りますか?

見られているポイント変更に強くする目的と、要素の探し方の工夫

Q3E2E テストで画面の要素を探すとき、何に気をつけますか?

模範回答

見た目や構造で変わりやすい CSS のクラス名や XPath は避けて、ボタンの役割と名前、入力欄のラベル、それで探せなければ開発者に付けてもらった data-testid で探します。役割と名前は利用者に見えるものと同じなので、デザインが変わっても壊れにくいです。

役割と名前で探せないときは、ボタンに名前がないなどのアクセシビリティの問題であることも多いので、テストを無理に書く前に、作りの側を直す提案をします。

深掘りされたら

  • data-testid を使う利点と欠点は?

見られているポイント壊れにくいロケーターの選び方

Q4E2E テストで毎回画面からログインしていて遅いです。どう改善しますか?

模範回答

ログインを最初に1回だけ行って、Cookie などのログイン状態をファイルに保存し、各テストはその状態から始めるようにします。Playwright なら準備用のプロジェクトで storageState を保存して使い回せます。ログイン画面そのもののテストは別に数本だけ残します。

あわせて、商品の登録のような前提のデータも画面ではなく API やシードで作るようにし、「確かめたいこと以外は画面を通さない」ようにします。これで時間が短くなるだけでなく、ログイン画面の不具合で全部のテストが落ちることも防げます。

深掘りされたら

  • 権限の違うユーザーのテストはどうしますか?

見られているポイント前提の作り方による高速化と安定化

Q5サーバーがエラーを返したときの画面表示を、自動テストでどう確かめますか?

模範回答

E2E の道具の通信の差し替えを使います。Playwright なら page.route でその API へのリクエストを止めて、ステータス 500 の偽の応答を返し、画面にエラーの文言が出ること、ボタンがもう一度押せることなどを確かめます。

同じやり方で、0 件のとき、応答がとても遅いときの表示も確かめられます。差し替えたテストは本物の API の不具合は見つけられないので、主要な流れは本物とつないだテストでも確かめる、と使い分けます。

深掘りされたら

  • タイムアウトの表示はどう確かめますか?

見られているポイント異常系を再現する工夫と、その限界の理解