前提
Selenium WebDriver と ChromeDriver を使うと、 Chrome の新規ウィンドウが適当なサイズで立ち上がるが、 width が微妙に狭い。さらにディスプレイのサイズとの兼ね合いで、 CSS のメディアクエリのブレークポイントをこえてしまうことがある。そのせいでハマった話。
結論
Chrome の起動オプションを指定してウィンドウサイズを固定すべし。
解説
Capybara から Selenium WebDriver を使って、とあるウェブアプリケーションに対するブラウザ自動化テストを書いていた。このウェブアプリケーションはレスポンシブ・ウェブデザインを採用しており、 Bootstrap v4 の hidden-*-up
や hidden-*-down
を利用している。
これらのユーティリティ・クラスを使って、画面サイズに応じてヘッダー部分のメニュー項目をハンバーガーメニューの中にしまいこむようになっている。ものすごく簡略化すると↓のようなマークアップになる。
自分の作業環境は Retina ディスプレイの MacBook Pro に DELL の U2212HW という外部ディスプレイを接続している。解像度はそれぞれこうなっている。
ChromeDriver によって立ち上げられる Chrome ウィンドウのサイズは、アクティブなディスプレイのサイズを元に決定されるらしく、 Retina ディスプレイ側で起動したときと外部ディスプレイ側で起動したときでサイズが異なる。
このサイズの違いがちょうど、ウェブアプリケーションの CSS メディアクエリのブレークポイントを超えてしまう数字だった。書いていたテストスクリプトの中で、ヘッダーメニューの要素の属性値をチェックする箇所があり、小さいウィンドウサイズの Chrome で実行したときだけ、対象の要素がハンバーガーメニューの中に隠れているのでテストが落ちてしまった。
Firefox はウィンドウサイズを指定せずとも十分大きなウィンドウサイズで起動し、 Chrome も Retina ディスプレイ側で起動されたときは問題ないため、一見すると「Chrome でだけランダムに落ちる」という状況で、原因の特定に苦労した。テストが落ちてもブラウザのウィンドウを閉じないようにした上で、開きっぱなしのウィンドウで Developer Tools のコンソールを起動して要素を探したりしているうちに、ヘッダーの見た目が違うことに気づき、解決に至った。
以下、アクティブなディスプレイの違いによって起動した Chrome のウィンドウサイズが異なる例。
ウィンドウサイズ指定無し・Retina ディスプレイ側で起動
テストスクリプト
Chrome のウィンドウサイズは 2100 ピクセル。 width が十分大きいので、 hidden-md-down
の要素が表示される。
ウィンドウサイズ指定無し・外部ディスプレイ側で起動
テストスクリプト
Chrome のウィンドウサイズは 927 ピクセル。 width がやや小さいので、 hidden-lg-up
の要素が表示される。
ウィンドウサイズ指定あり・Retina ディスプレイ側で起動
テストスクリプト
Chrome のウィンドウサイズは 2560 ピクセル(1280*2)。 hidden-md-down
の要素が表示される。
ウィンドウサイズ指定あり・外部ディスプレイ側で起動
テストスクリプト
Chrome のウィンドウサイズは 1280 ピクセル。 hidden-md-down
の要素が表示される。
教訓
- エラーメッセージを読んで意味を考えること。「要素がクリックできない」といっているのであれば、ブラウザからは本当に要素が見つけられていないということだ
- ブラウザの画面をよく見ること。超よく見ること。 sleep でもなんでも挟んで開きっぱなしにしたり、スクリーンショットを撮ったり、じっくり見るためにできることはある
- 要素が DOM 上に存在するかチェックするときコンソールから jQuery などで探す場合、
:visible
をつけること。これなしで要素にヒットしたとしても、それはブラウザから「見えている」とは限らない - 特別な理由がない限り、おとなしく Selenium WebDriver のデフォルトである Firefox を使っておくのが無難
- ただし、今回のケースではどうしても Chrome を使いたい事情があった。 Chrome でのテストカバレッジをあげるために Firefox ではパスするが Chrome で落ちる要素クリック処理まわりのテストをローカル環境でデバッグしていたのだ...