🖥️

簡単で便利なE2Eテスト自動化を夢見て(1)

に公開

FEConf2024で発表された〈簡単で便利なE2Eテスト自動化を夢見て〉をまとめた記事です。発表内容を2回に分けて公開します。第1回ではE2Eテスト、そのテストを支援するツール、そして保守コストを下げつつ効率的にテストコードを積み上げる方法を学びます。第2回ではテストコードの再利用とモジュール化、さらにPlaywrightの改善点を取り上げます。本文に挿入された画像の出典はすべて同じタイトルの発表資料であり、個別の出典表記はしていません。

〈簡単で便利なE2Eテスト自動化を夢見て〉
ペク・ブソク Stibee CTO

  • 簡単で便利なE2Eテスト自動化を夢見て (1)
  • 簡単で便利なE2Eテスト自動化を夢見て (2)

こんにちは。〈簡単で便利なE2Eテスト自動化を夢見て〉というタイトルで発表を担当するペク・ブソクです。私は自らを“実用主義プログラマ”と紹介しています。大企業のSIやホームショッピングコマースを経て、繰り返し作業を自動化し、時間のかかるタスクを最小化することに注力してきました。現在はメールマーケティングサービスを提供するStibeeでCTOを務めており、28億通以上のメール配信で得た互換性最適化の経験を踏まえ、E2Eテストについてお話しします。

本記事の主なトピックは以下のとおりです。

  • E2Eテスト自動化ツールの進化
  • E2Eテスト自動化と効率向上
  • フロントエンドプロジェクトの設定

一方、以下の内容は専門的には扱いません。

  • TDD
  • モバイル特化型E2E
  • テストCI/CDパイプラインの自動化

それでは本題に入りましょう。

E2E

E2Eとは

サービスを運営する際、バックエンドでもフロントエンドでもユニットテストを行うのが理想です。しかし現実には、すべてのレイヤーで完璧なユニットテストが実施されるケースは多くありません。最終的にユーザー視点でのテストを確立し、検証するにはE2Eテストが欠かせません。

img0.png

とはいえ、E2Eテストが難しい理由は下図のとおりです。ユニットテストはフロントエンド・バックエンドともにコストが比較的低く、開発者自身が素早く対応できます。しかしE2Eテストは統合テストにUIテストが加わるため、テストコードの作成に時間がかかり、実行速度も遅くなりがちです。

img1.png

それでも高い信頼性が得られるため、一定範囲はカバーすべきだと判断しました。

E2Eテストの流れ

E2Eテストのプロセスは下図のように進みます。テストを設計し、コードを書き、実行し、結果を確認するという流れです。設計は開発者の領域外なので本記事では触れません。主にテストコードを記述する際の課題と改善策に焦点を当てます。

同様に、テスト実行部分の最適化方法もいくつかありますが、本記事では重要項目として扱いません。最後に結果レポートとしてE2Eテストの結果を確認する段階があります。ある企業では動画で結果を確認しますが、私はスクリーンショットで検証できるほうが作業しやすいと考え、その方法を採用しました。

img2.png

テストコードを作成すると、最終的にはフロントエンド領域から始めざるを得ません。そこでこの部分を自動化してテストコードを生成できないか検討しました。さらにデバッグや修正を容易にし、モジュール化による再利用を図りました。

私のE2Eストーリー:始まり

私がE2Eに興味を持ち、改善を始めたのはSIプロジェクトを担当していた頃です。当時は各ブラウザのレンダリング方式が大きく異なり、WebKitさえ存在しない時代だったため、HTML・CSS・JavaScriptを少し修正するだけでブラウザ間で多数の問題が発生しました。その検証に多くの時間を費やすことになり、不便を感じて改善を決意しました。

img3.png

Selenium

まず採用したのはSeleniumというWebアプリケーションのテスト自動化ツールです。Seleniumではユーザーが操作を録画するだけでテストコードが自動生成されます。生成されたコードをDBに保存し、再生して再現するという手順を取りました。

img4.png

Seleniumをより効率的に使うため、画像比較のアイデアを思いつきました。ちょうど参加したハッカソンで、この構想を実装する機会がありました。

私はテスト中の各ステップでキャプチャを行う機能を開発し、ブラウザ間の差異やコード変更による問題箇所を把握できるようにしました。ただし、上記の画像のようにいくつかの制約もありました。

Puppeteer

Seleniumを使っていた頃、Puppeteerというツールがリリースされました。これはChromeベースのテストツールで、Seleniumより優れたネットワーク制御機能を持ち、より速くテストコードを生成できると期待しました。

しかしPuppeteerにも大きな問題がありました。それは内部的にコード生成をサポートしない点です。そのためテストコードの自動生成が難しく、拡張機能を自作してみましたが不十分で、結果的に他の開発者が作ったライブラリを採用しました。

img5.png

さらにテストコードを書くと、あるステップで失敗した際に最初からやり直すため時間が膨大にかかります。例を挙げると、ログイン→カートに商品追加→購入手続き中に失敗すると、再度ログインから繰り返す形です。

img6.png

この問題を解決するため、ブラウザのセッションストレージやローカルストレージに進行状況を自動保存し、ブラウザ起動時に読み込んで特定のチェックポイントから再開できる機能を追加しました。多くの工夫を施しましたが、Puppeteerには物足りない部分が多く、同僚には勧めにくいツールでした。

SeleniumとPuppeteerの限界

img7.png

結局、テストコードの自動化を実現するにはSeleniumとPuppeteerに限界がありました。特にセレクター精度に課題がありました。ReactやVueのように動的にクラス名を生成するフレームワークでは、CSSやXPathをセレクターに使うSeleniumやPuppeteerは適切とは言えませんでした。

Playwright

そんな課題をほぼ解決したのがPlaywrightです。Puppeteerを開発したメンバーがMicrosoftへ移籍し、Puppeteer 2.0として開発したのがPlaywrightだと考えると分かりやすいでしょう。開発者が「再び作るならもっと良くできる」と言うように、Playwrightには多くの改善が加わっています。

Playwrightの利点は多岐にわたります。複数ブラウザのサポート、テストの並列実行による時間短縮、多言語対応、強力なネットワーク制御でリクエストをインターセプトし改変できる点などです。

img8.png

中でも私が最重要視するのは、自動コード生成をサポートしていることです。

さらに、録画時にDOMのセレクターとしてCSSやXPathより高性能な方式を提供し、スクリーンショットの自動撮影やブラウザストレージにデータ保存する機能も標準搭載しています。

img9.png

ここからはPlaywrightの基本機能を詳しく見ていきましょう。

まずVS CodeにPlaywrightをインストールし、録画ボタンを押すとコードが自動生成されます。Puppeteerでも似た機能がありますが、録画内容が完璧に動作するとは限りません。その点、Playwrightは非常に満足度が高いです。

もう一つ重要なのは強力なレポート機能です。動画録画やスクリーンショット保存だけでなく、DOM確認が可能です。トラブルシューティングの際はDOMの状態を確認する必要がありますが、Playwrightではすべてのステップのスクリーンショットが保存されるため、HTML構造やコンソール、ネットワーク情報を容易に確認できます。

img10.png

E2Eの例

E2Eテスト環境

ここからは具体例です。多くのサービスに存在する会員登録やログインに加え、Stibeeで頻繁に行われるアドレス帳作成、メール作成、メール配信、メール受信を例に、E2Eテストをどのように進めるかを見ていきます。

E2Eテストを始める前に選択肢があります。テスト用環境をその都度立ち上げて実行する一時的な環境と、継続的に運用できる環境の2つです。

一時的な環境はDockerなどでDBとサーバーを立ち上げる方法で、テストケースが失敗する確率は非常に低いです。ただし毎回環境を作るのは現場への適用が難しいでしょう。

私は2番目の運用可能な環境、つまりどの企業のステージング環境や検証環境でもすぐにテストを実行して確認できる仕組みを選びました。これにより1か月前や1年前に登録したデータでもテストし、録画して検証できます。外部インターフェースで運用中の環境にも定期的にテストコードを実行し、問題を即座に検出できます。

テストコードの作成

テストコードは保守コストが高いことをご存じでしょう。そこでE2Eテストを書く際、保守コストを極力抑えるようにしました。また、データ操作やテスト専用APIの開発を最小限にし、テストコード変更時に追加コストが発生しないよう心掛けました。

保守コストを低減しないとテストコードが管理されず、結局「テストコードを書かない」という元の状態に戻りがちです。

img12.png

認証サービスのテストコード

E2Eテストで頻繁に登場する認証サービスを例に詳しく説明します。E2Eテストでは複数の認証フローに直面します。Gmail、Daum、NAVERなどのログインが典型です。テストボットからのアクセスは弾くように設計されているため、この壁を越える必要があります。追加APIなしでWebSocketベースの実際のメール認証を行うメールサービスを実装し、認証を通過できるようにしました。

次にリキャプチャ認証です。Puppeteer向けのライブラリもありますが不安定なため、外部サービスを利用しました。起動すると約1分間ブラウザが動き、リキャプチャを通過させてくれます。時間がかかるため追加でタイムアウト管理が必要です。

img14.png

最後はSMS認証です。保守コスト削減のため追加APIを避けたいところですが、SMS認証用のサービスは見つからず、最終的にAPIを実装しました。

SMS認証ではDBにある6桁のコードを検出する必要があります。どうせ作るならノーコードで便利なツールを試そうと考え、Zapierに似たオープンソースの自動化ツール「n8n」を採用しました。

img15.png

n8nについて少し補足します。Slackでの会話がそのままタスクになることが多いため、n8nを使い、メッセージにロード中の絵文字を付けると自動でNotionのタスクリストに追加し、絵文字を完了に変えるとタスクをクローズするワークフローを構築した経験があります。

img16.png

このような処理をコードで書くことも可能ですが、n8nなら下図のようにUIでドラッグ&ドロップするだけで簡単に自動化できます。保守も比較的容易です。

同様にSMS認証のプロセスもn8nで自動化しました。DBに電話番号を保存し、6桁のコードを取得して認証する流れをn8nで設定しました。保守も簡単でした。

ここにPlaywrightのコード生成を活用すれば、ほぼ完璧なテストコードを作成できます。

E2Eテスト、Selenium、Puppeteer、Playwrightといったテストツール、そして認証サービスや保守コスト削減のテクニックをご紹介しました。次回はE2Eテストの重要な最終段階であるモジュール化による再利用、さらにPlaywrightの改善点を解説します。


  • 簡単で便利なE2Eテスト自動化を夢見て (1)
  • 簡単で便利なE2Eテスト自動化を夢見て (2)

Discussion