Chrome 拡張機能: Service Worker の停止をテストする方法

内容

Manifest V2 から Manifest V3 への移行には、根本的な変更が伴います。Manifest V2 では、拡張機能はバックグラウンド ページに存在していました。バックグラウンド ページは、拡張機能とウェブページ間の通信を管理していました。マニフェスト V3 では、代わりに Service Worker が使用されます。

この記事では、拡張機能サービス ワーカーのテストに関する問題について詳しく説明します。特に、サービス ワーカーが停止された場合にプロダクトが正しく機能するようにする方法について説明します。

Google とは

eyeo は、ユーザー、ブラウザ、広告主、パブリッシャーにとってバランスの取れた持続可能なオンライン価値交換を実現することに専念している企業です。世界中で 3 億人を超える広告フィルタリング ユーザーが、Acceptable Ads の表示を許可しています。Acceptable Ads は、広告が許容され、邪魔にならないかどうかを判断する独立した広告基準です。

Google の拡張機能エンジン チームは、世界中で 1 億 1, 000 万人以上のユーザーが利用している AdBlock や Adblock Plus など、市場で最も人気のある広告ブロック ブラウザ拡張機能の一部を支える広告フィルタリング技術を提供しています。また、このテクノロジーはオープンソース ライブラリとして提供されており、他の広告フィルタリング ブラウザ拡張機能でも利用できます。

サービス ワーカーとは

拡張機能サービス ワーカーは、ブラウザ拡張機能の中心的なイベント ハンドラです。バックグラウンドで独立して実行されます。概ね問題ありません。バックグラウンド ページで行う必要があるほとんどの処理は、新しいサービス ワーカーで行うことができます。ただし、背景ページと比較していくつかの変更があります。

  • サービス ワーカーは使用されていないと終了します。そのため、グローバル変数に依存するのではなく、アプリケーションの状態を保持する必要があります。つまり、システムへのエントリ ポイントは、システムの初期化前に呼び出される準備ができている必要があります。
  • 非同期コールバックを待機する前に、イベント リスナーをアタッチする必要があります。停止されたサービス ワーカーは、登録したイベントを引き続き受信できます。イベントのリスナーがイベントループの最初のターンで登録されていない場合、そのイベントがサービス ワーカーを起動しても、イベントは受信されません。
  • アイドル状態の終了により、完了前にタイマーが中断される可能性があります。

サービス ワーカーはいつ停止されますか?

Chrome 119 では、サービス ワーカーが停止する事象が確認されています。

  • 30 秒間イベントを受信しなかった場合、または拡張機能の API を呼び出さなかった場合。
  • デベロッパー ツールが開いている場合や、ChromeDriver ベースのテスト ライブラリを使用している場合は、使用しないでください(機能リクエストを参照)。
  • chrome://serviceworker-internals で [停止] をクリックした場合。

最新情報については、サービス ワーカーのライフサイクルをご覧ください。

テストが問題となるのはなぜですか?

理想的には、「サービス ワーカーを効率的にテストする方法」や、動作するテストの例に関する公式ガイダンスがあると便利だったでしょう。サービス ワーカーのテストでは、いくつかの課題に直面しました。

  • テスト拡張機能に状態があります。サービス ワーカーが停止すると、その状態と登録されたイベントは失われます。テストフローでデータを保持するにはどうすればよいですか?
  • サービス ワーカーがいつでも停止される可能性がある場合は、中断された場合にすべての機能が動作することをテストする必要があります。
  • テストにサービス ワーカーをランダムに停止するメカニズムを導入しても、簡単に停止できる API はブラウザにはありません。この機能の追加について W3C チームに依頼しておりますが、現在も協議中です。

サービス ワーカーの停止のテスト

テスト中にサービス ワーカーの停止をトリガーするために、以下のアプローチを試しました。

アプローチ アプローチに関する問題
任意の時間を待機します(30 秒など) これにより、特に複数のテストを実行する場合、テストが遅くなり、信頼性が低下します。WebDriver を使用している場合は機能しません。これは、WebDriver が Chrome の DevTools API を使用し、DevTools が開いているときにサービス ワーカーが停止されないためです。バイパスできたとしても、サービス ワーカーが停止されているかどうかを確認する必要がありますが、その方法はありません。
サービス ワーカーで無限ループを実行する 仕様では、ブラウザがこの機能を実装する方法によっては、停止につながる可能性があることが規定されています。この場合、Chrome は Service Worker を終了しないため、Service Worker が停止されるシナリオをテストすることはできません。
サービス ワーカーに、停止されているかどうかを確認するメッセージがある メッセージを送信すると、サービス ワーカーが起動します。これは、サービス ワーカーがスリープ状態かどうかを確認するために使用できますが、サービス ワーカーを停止した直後にチェックを行う必要があるテストの結果が破棄されます。
chrome.processes.terminate() を使用してサービス ワーカー プロセスを強制終了します。 拡張機能の Service Worker は拡張機能の他の部分とプロセスを共有するため、chrome.process.terminate() または Chrome のプロセス マネージャー GUI を使用してこのプロセスを終了すると、Service Worker だけでなく拡張機能のページも終了します。

最終的に、Selenium WebDriver で chrome://serviceworker-internals/ を開き、サービス ワーカーの [停止] ボタンをクリックして、サービス ワーカーの停止に対するコードの応答を確認するテストを作成しました。

これは現時点では最善の方法ですが、Mocha テスト(拡張機能ページで実行) ではこの操作を実行できないため、WebDriver ノード プログラムに通信を返す必要があるため、理想的ではありません。つまり、これらのテストは拡張機能のみを使用して実行することはできず、Selenium WebDriver を使用してトリガーする必要があります。

次の図は、さまざまなフローを通じてブラウザ API と通信する方法と、「サービス ワーカーの停止」メカニズムを追加した場合の影響を示しています。

テストフローを示す図
サービス ワーカーの停止によるテストフロー。

Service Worker を停止する新しいフロー(青)では、UI で停止を「クリック」してブラウザ API でアクションをトリガーする Selenium WebDriver を追加しました。

なお、Selenium WebDriver でこの操作を行うと、サービス ワーカーを再起動できなくなるChrome のバグがありました。この問題は Chrome 116 で修正されましたが、幸い回避策もあります。すべてのタブで DevTools が自動的に開くように Chrome を設定すると、Service Worker が正しく起動します。

ボタンのクリックが安定した API ではない場合や、DevTools を開くとパフォーマンスに影響する可能性があるため、これは理想的ではありませんが、Google ではテスト時にこのアプローチを使用しています。

すべての機能をカバーするにはどうすればよいですか?ファズテスト

停止をテストするメカニズムを構築した後、それを自動化テストスイートに組み込む方法を決定する必要がありました。バックグラウンド ページを操作するたびに、WebDriver が chrome://serviceworker-internals/ ページで [停止] をクリックしてサービス ワーカーが停止される環境で、標準テストを実施しました。

サンプルの fuzz テスト実行
テストの現在の設定を示す画像。

停止メカニズムは完全に安定しておらず、不安定になることがあるため、すべてのテストではなく、ほとんどのテストのみを実行しています。また、すべてのテストスイートをファズモードで実行すると、時間がかかる可能性があります。そのため、すべての「類似」ケースを網羅するのではなく、ファズモードでテストする最も重要なパスを選択しました。なお、機能テストを「ファズ」モードで実行する場合、サービス ワーカーの停止と再起動に追加の時間がかかることから、テストのタイムアウトを長くする必要がありました。

これらのテストは、コードが失敗する多くの場所をハイライト表示する、大まかな最初のパスとして有用ですが、サービス ワーカーの停止によって問題が発生する可能性のある微妙な方法をすべて検出できるとは限りません。

Google 社内では、このようなテストを「ファズテスト」と呼んでいます。従来、ファズ テストでは、プログラムに無効な入力を送り込み、プログラムが適切に応答するか、少なくともクラッシュしないことを確認します。Google のケースでは、「無効な入力」とは、サービス ワーカーがいつでも停止されることであり、「妥当な動作」とは、広告フィルタ機能が以前どおりに機能し続けることです。これは Manifest V3 では想定される動作であるため、厳密には無効な入力ではありませんが、Manifest V2 では無効だったため、妥当な用語であると考えられます。

概要

サービス ワーカーは、(declarativeNetRequest ルール以外に)Manifest V3 の最大の変更点の 1 つです。Manifest V3 への移行には、ブラウザ拡張機能のコード変更やテスト方法の変更が必要になる場合があります。また、永続状態を持つ拡張機能のデベロッパーは、予期しないサービス ワーカーの停止を適切に処理できるように拡張機能を準備する必要があります。

残念ながら、お客様のユースケースに適した簡単な方法で停止を処理するための API はありません。初期段階で、停止メカニズムに対する拡張機能のコードベースの堅牢性をテストしたかったため、回避策を講じなければなりませんでした。同様の課題に直面している他の拡張機能デベロッパーは、この回避策を使用できます。この回避策は、開発とメンテナンスの段階では時間がかかりますが、サービス ワーカーが定期的に停止される環境で拡張機能を確実に動作させることができるため、その価値はあります。

すでにサービス ワーカーの停止テストの基本的なサポートはありますが、拡張機能内からサービス ワーカーのテストに関するプラットフォームのサポートを強化することは、テストの実行時間とメンテナンス作業を大幅に削減できるため、今後の課題と考えています。