メインコンテンツへスキップ

E2Eテストが16件落ちた。原因は全部テスト側だった|Playwright運用の実際

基盤の移行から2日後にE2Eテストが16件落ちた。調べると、製品側の不具合はゼロで、原因は全部テスト側だった。同じ時期、製品側には9件の実害が出ていたが、そちらはテストで捕まらなかった。編集部が177件のテストを運用して分かったことを記録する。

目次

この記事の立ち位置

テスト自動化の記事は、たいてい「導入すると品質が上がる」という前提で書かれている。実際に運用して、何が捕まって何が捕まらなかったかの記録は少ない。

編集部は複数のサービスでブラウザ自動操作によるテストを運用している。合計177件ある。

この記事は、基盤を移行した直後に16件が落ちたときの記録である。結論から書くと、製品側の不具合はゼロだった。原因は全部テスト側だった。

そして、同じ時期に製品側で9件の実害が出ていたが、そちらはテストで捕まっていなかった。

16件落ちて、製品側の不具合はゼロだった

配信基盤を全面的に移行した2日後、定期実行していたE2Eテストのうち16件が失敗した。

移行直後なので、当然「移行で何か壊れた」と考える。1件ずつ調べた結果、製品側の不具合は1件もなかった。

失敗したテストが確かめようとしていた機能を手で操作すると、すべて正常に動いた。

何が原因だったか

16件の原因を分類する。

1. 画面の文言が変わっていた

ボタンの表示が「ドラフトを作る」から「ドラフト作成」に変更されていた。テストは古い文言で要素を探していた。

文言の変更は、機能の変更ではない。 しかしテストは落ちる。

2. 古いパスワードを使っていた

テスト用のアカウントのパスワードが変更されており、ログインできなくなっていた。

3. 認証方式の変更に未対応

管理画面に二段階認証が追加されていた。テストは対応していない。

4. 要素の指定が決め打ちだった

画面上の要素を、識別子の値を直接書いて指定していた。データが変わると識別子も変わるため、見つからなくなった。

5. 閾値を取り違えていた

入力欄の文字数の上限が200字に拡大された。テストは「上限を超える入力を弾くこと」を確かめるものだったが、172字しか入力していなかった。 拡大後は上限に達しないため、弾かれずに失敗した。

共通するもの

いずれも、製品の仕様が正しく変わったことに、テストが追随していなかった。

これは「テストが悪い」という話ではない。仕様が変わればテストも変える必要があるという、当たり前の作業が漏れていただけである。

ただし運用上は重要な意味を持つ。移行直後に16件落ちたという事実は、移行が失敗したように見える。 実際には無関係だった。

同じ時期、製品側には9件の実害があった

対照的なのがこちらである。同じ移行で、製品側には実際の不具合が9件出ていた。

代表的な3件

書き込みのときだけ失敗する。 データベースを移行した際、既定値の設定が新しい基盤のSQLに反映されていなかった。読み取りは正常に動くため、画面を開いても異常に見えない。書き込みを試した利用者だけがエラーになる。

APIの項目名が変わり、画面側で例外が出た。 応答に含まれる項目の名前が変わった、あるいは項目自体が無くなった。受け取る側は配列を前提に処理しており、存在しない項目に対する処理で例外になった。

非同期処理が利用者の入力を引き継がなかった。 処理を分割して実行する仕組みに移行した際、渡す情報だけから状態を組み立てる設計になっていた。結果として、利用者が回答した内容が、後続の生成処理に届いていなかった。

これらはテストで捕まっていない

16件のテストが落ちている裏で、9件の実害が通過していた。

なぜテストで捕まらなかったか

3件それぞれ、捕まらなかった理由が違う。

読み取りしか試していなかった

画面が開くこと、一覧が表示されることは確認していた。書き込みを試すテストが無かった。

書き込みのテストは、後始末が必要になるため書かれにくい。作ったデータを消す処理を書き、失敗したときにも消えるようにする必要がある。面倒なので後回しになる。

正常系しか通っていなかった

項目名が変わった件は、変わった項目を使う画面に到達するテストが無かったために通過した。

到達する経路が深いほど、テストは書かれにくい。ログインして、検索して、詳細を開いて、編集する。深い経路ほど、実害も大きい。

結果の中身を見ていなかった

非同期処理の件は、処理が完了したことは確認していたが、完了した中身は見ていなかった。

生成された文書が返ってくること自体は成功する。その中身に利用者の回答が反映されているかは、別の検査が要る。

「動いた」と「正しく動いた」は別の量である。

落ちるテストと落ちない不具合

この2つを並べると、非対称がはっきりする。

  テストが落ちる テストが落ちない
製品が正常 16件 文言変更・古い認証情報 大多数(正常)
製品が異常 理想的に検出できる状態 9件 書き込み・深い経路・中身

左下(製品が異常でテストが落ちる)が、本来テストに期待している働きである。 ところが実際に起きたのは、右上と右下だった。

この非対称が意味すること

テストが落ちたことは、製品の異常の証拠にならない。 逆に、テストが全部通ったことも、製品が正常である証拠にならない。

どちらも当たり前に聞こえるが、運用ではこの2つを混同しやすい。「全部緑だから大丈夫」と読んでしまう。

テストの役割を言い直す

テストは、「以前できていたことが、まだできること」を確かめる道具である。

新しく壊れた箇所のうち、テストが見ている範囲にあるものだけが検出される。範囲の外は何件テストがあっても検出されない。

だから、件数を増やすより、範囲を確かめるほうが効く。

切り分けの手順

テストが落ちたときに、編集部が踏んでいる順序である。

1. 手で操作する

失敗したテストが確かめようとしていた操作を、実際に手で行う。

手で通るならテスト側の問題。手でも通らないなら製品側の問題である。

この1手順で、16件の切り分けが終わった。

2. 落ちた理由を分類する

要素が見つからない、待ち時間で失敗した、値が想定と違う、認証で失敗した。分類すると、同じ原因でまとめて直せることが多い。

16件のうち、文言変更が原因のものは複数あった。1か所直せば全部通る。

3. 製品側だった場合は、テストを先に直さない

これが重要である。製品側の不具合なのにテストのほうを直すと、本物の不具合をテストごと消してしまう。

「テストが落ちるから期待値を変える」は、最も危険な直し方である。

4. 直したら、壊れた状態で確かめる

テストを直した後、意図的に壊した状態で落ちることを確認する。

編集部では、壊れたデプロイを対象にテストを回して4件が失敗すること、正常な状態では15件すべて通ることを確認したことがある。片方だけでは、何も検出しないテストになっていても気づけない。

運用している177件の内訳

実際の構成である。

対象 形式 件数
主要サービス TypeScript(15ファイル) 95
主要サービス Python(9ファイル) 60
メディアサイト Python(1ファイル) 22

2つの言語で書かれているのは、歴史的な経緯による。統一したほうがよいが、動いているものを止めてまで統一する優先度は高くない。

代表的なテストの内容

本番環境の煙感知。 健全性の確認、主要な5画面が開くこと。

不具合の再発防止。 過去に直した不具合について、同じことが起きないかを確かめる。選択した内容が保持されるか、文字数の制限が効くか。

生成処理の統合テスト。 一連の流れが最後まで通るか、エラーのときに適切に扱われるか。6つの経路を確認している。

管理画面の同期履歴。 データの同期記録が正しく残るか、管理者としてアクセスできるか、集計が表示されるか。

診断機能。 未ログインでのアクセスが弾かれるか、判定結果が正しいか、連続実行の制限が効くか。

最も価値があるのは煙感知テスト

177件のうち、最も価値があるのは本番環境に対する5件程度の煙感知テストである。

理由

壊れたときに最も困る経路を押さえている。 トップページが開く、ログインできる、主要な機能の画面が表示される。

実行が速い。 数分で終わるので、頻繁に回せる。

本番に対して実行している。 検証環境で通っても本番で壊れていることがある。

件数を増やす前にやること

テストを増やそうとする前に、「これが壊れたら事業が止まる」という経路を1つ挙げる。 その1つを押さえるテストが既にあるかを確かめる。

無ければ、それを最初に書く。細かい機能の網羅は、その後でよい。

テストデータが足りない問題

運用で最も詰まるのがここである。

編集部では、9ファイルにまたがってテストを一時的に飛ばしている。 理由の大半はデータ側の事情である。

典型的な理由

必要な種別のアカウントが登録されていない。 ある役割でしかできない操作を確かめたいが、その役割のアカウントが無い。

パスワードが分からない。 過去に作ったアカウントの認証情報が引き継がれていない。

検索結果が0件になる。 データが入っていないため、一覧が空で、その先の操作に進めない。

対処の方向

テスト用のデータを作る処理を、テストの中に持つ。 実行前に必要なデータを作り、終了時に消す。

ただしこれは本番環境では使えない。本番に対するテストは、読み取りと、既存データでできる操作に限る。

検証環境を本番に近い状態に保つ。 データが空の検証環境では、深い経路のテストが書けない。

飛ばすこと自体は現実的である

すべてを揃えてからテストを書こうとすると、いつまでも書けない。書けるものから書き、書けないものは飛ばす。

問題は、飛ばし方である。

飛ばすなら理由を書く

編集部の運用ルールである。検証できないテストは飛ばしてよい。ただし理由を必ず書く。

理由のない飛ばしは忘れられる

理由が書かれていないと、後から見た人が「なぜ飛ばしているのか」を判断できない。判断できないものは、そのまま残る。

半年後には、飛ばしているテストが何件あるかも分からなくなる。

書くべき内容

何が足りないか。 アカウント、データ、環境。

誰が用意できるか。 自分で用意できるのか、他の人に依頼する必要があるのか。

いつ見直すか。 期限を書く。書かないと見直されない。

飛ばしている件数を数える

定期的に、飛ばしているテストの件数を数える。 増え続けているなら、テストの仕組みが実態に合っていない。

壊れやすいテストの書き方

16件の失敗から、避けるべき書き方が分かる。

文言で要素を探す

ボタンの表示文字で探すと、文言の変更で落ちる。変更されにくい属性で探す。

要素の指定方法は公式の推奨にまとまっている。役割と名前で探す方法は、実装の変更に比較的強い。アクセシビリティの属性を使う考え方も同じ方向である。

識別子を決め打ちする

データベースの識別子を直接書くと、データが変わると落ちる。テストの中で作ったデータの識別子を使う。

順番に依存する

一覧の何番目、という指定は、データが増減すると壊れる。内容で特定する。

待ち時間を固定する

「3秒待つ」と書くと、遅い環境で落ち、速い環境では無駄に待つ。条件が満たされるまで待つ方式にする。待機と検証の書き方に、条件を指定する方法がある。

閾値を中途半端に試す

前述の172字の例である。上限を確かめるなら、上限ちょうどと上限プラス1で試す。 適当に大きい値を入れると、上限が変わったときに意味を失う。

全体の指針

避け方をまとめた推奨事項が公式にある。一度読んでから書くと、後の修正がかなり減る。

実行環境の固定

運用上の落とし穴を1つ記録しておく。

編集部の構成では、ブラウザ自動操作のライブラリのバージョンを設定ファイルで固定している。しかし実際にインストールされるのは、より新しいバージョンだった。指定の書き方が、上位互換を許す形式だったためである。

新しいバージョンでは、使用しているOSの版に対応するブラウザが提供されていなかった。 結果として、継続的な実行の仕組みに組み込めない状態になった。

対策

バージョンを厳密に固定する。 上位互換を許す指定は、ライブラリによっては破壊的な変更を引き込む。

OSとブラウザの組み合わせを記録する。 継続的実行の設定には、対応する環境の情報がある。組み合わせを記録していないと、動かなくなったときに原因を辿れない。

複数の言語で動かす場合

編集部ではTypeScript版Python版の両方を使っている。Python側はpytestから呼び出している。

それぞれ独立してバージョンが上がる。 片方だけ更新して挙動が変わることがある。

テスト自体が空振りしていないか

最後に、最も見落としやすい問題を書く。

テストが通っていることと、テストが実行されていることは別である。

編集部で起きたこと

アクセス解析のタグが発火していない不具合があった。それを確認するテストは、以前から存在していた。

しかし実行されていなかった。存在はしていたが、定期実行の対象に入っていなかった。

さらに、監視の説明文にはそのテストが例として書かれていた。 読むと「確認している」ように見える。例は検査ではない。

対象が0件でも通る

別の例では、検査の対象ディレクトリの指定を誤っており、対象0件のまま「問題なし」で正常終了していた。

合格の表示は、欠陥が無いことではなく、検査が届いていないことを意味する場合がある。

確かめ方

実行した件数を出力させる。 「15件実行、15件成功」と出るなら、0件でないことが分かる。

対象が0件なら失敗として扱う。 これを入れると、空振りが検出できる。

定期実行のログで、実際に何件走ったかを確認する。 設定ファイルに書いてあることと、走っていることは別である。

基盤を移行するときに何を確かめるか

今回の16件は、基盤の全面移行がきっかけだった。移行という作業に特有の確認事項を整理しておく。

読み取りだけで確かめない

9件の実害のうち1件は、読み取りは正常、書き込みだけ失敗という形だった。データベースの既定値の設定が、新しい基盤のSQLに反映されていなかった。

移行後の確認では、画面が開くことを見て安心しがちである。書き込みを1件試す。 これだけで、この種の不具合は捕まる。

応答の項目名を突き合わせる

移行でAPIの実装が変わると、応答に含まれる項目の名前や有無が変わることがある。受け取る側は、それを前提に処理している。

移行前後で、代表的なAPIの応答を保存して差分を取る。 項目名の変更は目視で分かる。

非同期の処理は、渡している情報を確かめる

処理を分割して実行する仕組みに移すと、渡す情報だけから状態を組み立てる設計になることがある。

編集部の例では、利用者が回答した内容が後続の処理に届いていなかった。処理が完了することは確認できても、中身は別である。

キャッシュを迂回して確かめる

移行直後は、古い内容がキャッシュから返ることがある。キャッシュを迂回した経路でも確認する。

この13時間の障害を含む、HTTP 200を返しながら壊れていた6件はHTTP 200なのに壊れているに記録した。

編集部では、配信物が13時間失われていたにもかかわらず、入口のURLはキャッシュから200を返し続けていた例がある。

テストの失敗を移行の失敗と読まない

今回の教訓である。移行直後にテストが落ちたら、まず切り分ける。

16件すべてがテスト側の問題だったにもかかわらず、最初は「移行で壊れた」と考えた。切り分けに時間を使ったが、その時間で製品側の9件を探すこともできた。

移行前にテストを直しておく

理想を言えば、移行の前にテストを最新の仕様に追随させておく。 そうすれば、移行後に落ちたテストは移行が原因だと判断できる。

追随していないテストを抱えたまま移行すると、移行の影響とテストの陳腐化が混ざる。 切り分けの費用がかかるのはこのためである。

費用はどこにかかるか

テスト自動化の費用は、書く時間ではない。書いた後の維持にかかる。

維持の費用が見えにくい

編集部の16件の失敗は、製品の仕様が正しく変わったことに追随する作業だった。文言を変えれば、それを使うテストを直す。認証方式を変えれば、ログインの処理を直す。

この作業は、機能開発の見積もりに入っていないことが多い。「テストを書く工数」は見積もられるが、「テストを直し続ける工数」は見積もられない。

壊れやすいテストほど高くつく

前述の壊れやすい書き方をしたテストは、仕様が変わっていなくても落ちる。 データが変わった、表示順が変わった、実行環境が遅かった。

落ちるたびに調べる時間がかかり、しかも多くは製品の問題ではない。この繰り返しが続くと、落ちても誰も見なくなる。

これが最悪の状態である。テストがあるのに信用されていないと、費用だけが残る。

元を取れる条件

テスト自動化が元を取るのは、同じ確認を繰り返す場合である。

デプロイのたびに手で10画面を確認しているなら、自動化する価値がある。年に1回しか変わらない機能なら、手で確認するほうが安い。

頻度で判断する。 網羅率で判断すると、維持できない量になる。

本番に対する読み取りのテストは費用対効果が高い

編集部の経験では、本番環境に対して主要な画面が開くことを確認する数件が、最も費用対効果が高い。

書くのが簡単で、壊れにくく、壊れたときの意味が明確である。

人手の確認と、どう分けるか

自動化で全部やろうとすると破綻する。分ける基準を決める。

自動化に向くもの

判定が機械的なもの。 200が返る、要素が存在する、件数が一致する。

繰り返すもの。 デプロイのたび、日次。

壊れたときの影響が大きいもの。 ログイン、決済、申し込み。

人手に残すもの

見た目の判断。 レイアウトの崩れ、色の違和感。機械で判定しようとすると、微細な差で落ち続ける。

文章の意味。 生成された文章が妥当か。

初めて作った機能。 仕様が固まる前にテストを書くと、書き直しになる。

編集部の運用

レイアウトの確認には、画像ではなく構造を文字列で取得して比較する方法を使っている。見た目の比較は差分が出やすく、維持できないためである。

機能の確認はブラウザ自動操作、構造の確認は別の道具、と分けている。1つの道具で全部やろうとしない。

AIにテストを書かせる場合

生成AIにテストを書かせる使い方がある。編集部でも行っている。

向いている部分

既存のテストと似たものを増やす。 型が決まっていれば、機械的に増やせる。

壊れたテストの修正。 文言が変わった、識別子が変わったといった修正は、差分が明確なので任せられる。

向いていない部分

何をテストすべきかの判断。 これは仕様の理解が要る。

落ちたテストの切り分け。 製品側かテスト側かの判断は、手で操作して確かめる必要がある。

危険な使い方

落ちているテストを「通るように」直させること。 これをやると、期待値のほうが書き換えられる。

本物の不具合が、テストごと消える。

編集部では、テストの修正を任せる場合、期待値の変更を禁止する指示を明示している。修正してよいのは、要素の指定方法と、手順の部分だけである。

任せるときの条件

前提として、完了の確かめ方を渡す。 「テストが通ること」を条件にすると、通すために期待値が変わる。

「手で操作して同じ結果になることを確認し、その手順を報告に書く」のように、機械的に通せない条件にする。

完了の判定方法を渡すことが効いた実測はプロンプトは足しても良くならないに、任せる作業と任せない作業の線引きはAIエージェントとはにある。

導入するときの順序

これから入れるなら、次の順序を勧める。

1. 壊れたら事業が止まる経路を1つ挙げる

網羅を考える前に、1つ決める。

2. その経路を本番に対して確認するテストを書く

本番に対してが要点である。検証環境で通っても本番で壊れていることがある。読み取りのみに限れば、本番でも安全に実行できる。

3. 実行する場所に埋める

手で実行するテストは、実行されなくなる。デプロイの手順か定期実行に組み込む。

4. 壊れた状態で落ちることを確認する

書いたら、意図的に壊して落ちることを確かめる。

5. 落ちたときの切り分け手順を決める

誰が、何分以内に、どう切り分けるか。 決めていないと、落ちたまま放置される。

編集部では、監視が15分ごとに正しく異常を通知していたにもかかわらず、13時間対応されなかったことがある。検知と対応は別の問題である。

6. その後で件数を増やす

ここまでできてから、網羅を考える。

まとめ

E2Eテストを177件運用した記録である。

基盤移行の2日後に16件落ちたが、製品側の不具合はゼロだった。 文言変更への未追随、古い認証情報、認証方式の変更、識別子の決め打ち、閾値の取り違え。

同じ時期、製品側には9件の実害があったが、テストで捕まっていなかった。 書き込みを試していない、深い経路に到達していない、結果の中身を見ていない。

テストが落ちたことは製品の異常の証拠にならず、全部通ったことも正常の証拠にならない。 混同しやすい。

製品側の不具合なのにテストを直すと、本物の不具合を消してしまう。 落ちたらまず手で操作して切り分ける。

飛ばすなら理由を書く。 理由のない飛ばしは忘れられる。編集部でも9ファイルで飛ばしている。

テストが存在することと、実行されていることは別の量である。 対象0件でも「問題なし」で通る。

明日から試せる3つ

1つ目は、「これが壊れたら事業が止まる」経路を1つ挙げ、それを確認するテストがあるか調べることである。無ければ、それが最初に書くべき1件である。

2つ目は、いま飛ばしているテストの件数を数えることである。理由が書かれていないものがあれば、それは忘れられている。

3つ目は、テストを1つ意図的に壊して、落ちることを確認することである。落ちなければ、そのテストは何も見ていない。

編集部では、AI導入の支援に加えて、検証の仕組みづくりも行っている。自動化は入れたが何を保証しているか分からない、という段階の相談については、サービス案内を参照してほしい。

参考にした一次資料

よくある質問

E2Eテストが落ちたら製品が壊れているということですか。

限りません。編集部の記録では、基盤移行後に16件のテストが失敗しましたが、調べた結果、製品側の不具合はゼロでした。原因は全てテスト側で、画面の文言変更への未追随、古いパスワード、認証方式の変更への未対応、要素の指定方法の決め打ち、閾値の取り違えでした。

テストが全部通れば製品は正常ですか。

これも限りません。上記と同じ時期、製品側には9件の実害が出ていましたが、テストでは捕まっていませんでした。書き込み時だけ失敗する、APIの項目名が変わってフロント側で例外が出る、非同期処理が利用者の入力を引き継がないといった不具合です。テストが見ている範囲の外にある不具合は、何件通っても検出されません。

テストが落ちたときに何から調べるべきですか。

製品側かテスト側かの切り分けです。編集部では、失敗したテストが確かめようとしていた機能を、手で実際に操作します。手で通るならテスト側の問題で、手でも通らないなら製品側の問題です。この切り分けをせずにテストを直すと、本物の不具合をテストごと消してしまいます。

テストデータが足りなくてテストが書けません。

編集部でも同じ問題があり、9ファイルにまたがってテストを一時的に飛ばしています。理由の大半は、必要なアカウントが登録されていない、パスワードが分からない、検索結果が0件になるといったデータ側の事情です。飛ばすこと自体は現実的ですが、必ず理由を書いて残してください。理由のない飛ばしは、そのまま忘れられます。

テストは何件くらい必要ですか。

件数より、何を見ているかが重要です。編集部が運用しているのは合計177件ですが、そのうち最も価値があるのは、本番環境に対して主要な画面が開くことを確認する5件程度の煙感知テストです。件数を増やす前に、壊れたときに最も困る経路が1つでも押さえられているかを確かめてください。

テストの実行環境で問題が起きることはありますか。

あります。編集部では、ブラウザ自動操作のライブラリのバージョンを固定していますが、実際にインストールされるバージョンが新しくなり、そのバージョンでは使用しているOSに対応するブラウザが提供されていない、という状態になったことがあります。ライブラリのバージョンとOSの組み合わせは、固定して記録してください。

関連する取り組み

CONNECTED SERIES
AIで投資の壁を越える
18 本の実装記録。AI 投資の「予測不能」と言われる 9 つの壁を、コードと実データで検証した連載。
note で読む →
CONSULTING
AI導入の無料相談
ALLFORCES が、本記事のような失敗パターンを回避する AI 導入支援を提供しています。まずは課題を聞かせてください。
問い合わせる →