Playwright で Blazor Server の E2E テストを書いたらテスト失敗するようになってしまった!
Blazor アプリケーションのエンド・ツー・エンド (End-to-End, 略して E2E) テストを実装する際、ここ最近は Selenium ではなく、Playwright (の .NET バインディング) を使って Web ブラウザの自動操作を実装しています。
先日も、とある手持ちの Blazro Server アプリケーションの E2E テストを、Seleinum を使って実装していたところを、Paywright を使った実装に置き換えたりしていました。
すると、その Playwright を使うように変更した Blazor Server アプリケーションの E2E テストが失敗するようになったのです。
例えば、Blazor プログラマであればお馴染みであろう (?)、例の「カウンター」ページの E2E テストを実装しているとしましょう。この「カウンター」ページの「Click me」ボタンをクリックしたら、カウンターの数字が 0 から 1 になっていることをテストする、とします。
Selenium で実装していたころはこのテストはもちろん成功していました。しかし、Playwright に置き換えて以降、このテストが失敗するようになってしまいました。失敗の原因は、カウンターの数字が 0 のままで、1 に加算されない、というものです。「Click me」ボタンが見つからないとかそういう原因ではないのです。一体何が起きているのでしょうか。
もしかして... Playwright が早すぎる!?
いろいろ探っていくうちに、どうやら、次のような動作になっているのではないかと推測されるに至りました。
- (既定の実装だと) Blazor Server はサーバー側でプリレンダリングされる
- Playwright で自動操縦されている Web ブラウザが、このプリレンダリングされたコンテンツを読み込んで表示する
- ただし、プリレンダリングされたコンテンツを読み込んで表示した直後は、サーバー側との SignalR 接続が確立され、かつ、すべてのイベントハンドラが配線される前のタイミングが存在する!
- Playwright の動作が高速であることによって、この Blazor Server 起動時の絶妙なタイミングで、Playwright が (プリレンダリングされた)「Click me」ボタンをクリックしてしまう?
- しかし、「Click me」ボタンは (プリレンダリングされているので) たしかに存在するものの、イベントハンドラは配線前なので、ボタンクリックに対するカウンター加算は起動されない。結果、「Click me」ボタンをクリックしてもカウンターは 0 のまま!
上記仮説が正しいとして、サーバー側プリレンダリングを止めると、このようなことは起きなくなるのかもしれません。しかし、せっかくのサーバー側プリレンダリングはこのまま維持したいため、プリレンダリングを止めるという選択は避けたいです。どうしたらよいでしょうか。
とりあえずの回避策を実装したものの...
今のところは、Blazor Server アプリケーション側に以下の "仕込み" を入れて、この E2E テストの失敗を回避するようにしました。
-
console.log
を呼び出せる JavaScript ヘルパ関数を用意 - Blazor Server の
MainLayout.razor
にて、OnAfterRenderAsync
のタイミングでIJSRuntime.InVokeAsync
を使って上記 JavaScript ヘルパ関数を呼び出し、"The Blazor Server app has been started." というテキストをconsole.log
で出力 - Playwright による E2E テスト実装側では、ページナビゲーション後、その
console.log
出力をWaitForConsoleMessage
で待機してから、続くボタンクリックなどのテストを実行する
Blazor Server 側で Razor コンポーネントのライフサイクルメソッド OnAfterRenderAsync
が呼び出されるタイミングは、ブラウザ側との接続も確立され、初回のレンダリングが完了した後になりますから、Playwright による E2E テストもこのタイミングに歩調を合わせることで、テストが安定化したと考えられます。
もっとマシなやり方があれば教えてもらえると嬉しいです!
...ということで、どうにかこの「Playwright の初動が早すぎてテストが失敗する」問題をねじ伏せたものの、本来であれば無用な console.log
出力があるのが納得いきません。Blazor Server 側にこのような "実装" "改造" が必要なのも、汎用性がなくイヤです。E2E テスト実装側でページナビゲーション後に数百ミリ秒の待機を差し込むことでも改善できるかもしれませんが、これまた、そのような微妙なマジックナンバーの待機を挟むのも、もうこりごりです。
何か他に上手い方策はないでしょうか、もっとマシなやり方があれば教えてもらえると嬉しいです!