headlessでseleniumを非表示実行する

2016 年 4 月 11 日 by 山平

Seleniumでブラウザを使った自動テストは便利ですが、GUIがシビアなWebアプリケーションの実行は気を使います。

たまたまマウスカーソルがSelenium実行中のブラウザの上に乗ってしまったことで、onMouseOver/onMouseOut等のイベントが走って、自動テストがエラーになってしまい、テストのやり直しに…なんてことが、私はありました。

一時期話題になったPhantomJSなどのヘッドレスブラウザは、実行中のブラウザが表示されないため、Selenium実行中のうっかりミスをゼロにしてくれる上に、描画がない分、高速に動作するというメリットもあり、一時期話題になりました。

ですが、見えないブラウザというのはメリットでもあり、デメリットでもあります。

まず、テストコードの誤りに気づきにくい点。

ブラウザが見えていればへんな待ち状態に入ってしまっているのにすぐ気づくことができますが、見えないためにタイムアウトするまで何が起きているのか気づくことができません。
テストコードの作成中はブラウザが見えているほうがはるかにデバッグしやすいです。

この問題は、開発中と自動テスト実施中で利用するブラウザを変更することで回避できるのですが、もう一つの問題を生む原因にもなります。

それは、ブラウザの違いによる動作の差です。

PhantomJSは出始めのころ、バグや未実装の機能のために、「今後に期待」というような言い回しがなされることもありました。
その後どうなったかは知りませんが、主要ブラウザと肩を並べるほどに開発が進んだとしても、開発と運用で別のブラウザを使うリスクがゼロになることはありません。

さて、前置きが長くなってしまいました。

上のような問題があるためヘッドレスブラウザは使いたくない、でも実行中のブラウザは邪魔といった場合にとても有用なツール「headless」をご紹介します。

unix系、というかXWindowSystemというか、仮想フレームバッファ(xvfb)という考えが昔からあります。
サーバに接続する環境の事情でGUIが使えない場合に、メモリに仮想的な画面を作ってGUIを使えるようにすることができます。

例えばサーバがCUIなのだけれどクライアントはGUIで使わせたい、という場合などに仮想フレームバッファに中継させることで実現可能です。

上のheadlessはrubyから簡単にxvfbを利用できるよう様にしたツールのようです。
headlessを有効にした状態でXに依存するアプリケーションを実行すれば、xvfbで処理されるようです。

RUBY:
  1. require 'rubygems'
  2. require 'headless'
  3. require 'selenium-webdriver'
  4.  
  5. Headless.ly do
  6. driver = Selenium::WebDriver.for :firefox
  7. driver.navigate.to 'http://google.com'
  8. puts driver.title
  9. end

上のサンプルプログラムをirbで実行してみました。

RUBY:
  1. irb(main):001:0> require 'headless'
  2. => true
  3. irb(main):002:0> require 'selenium-webdriver'
  4. => true
  5. irb(main):003:0>   driver = Selenium::WebDriver.for :firefox
  6. => #<Selenium::WebDriver::Driver:0x..fb796422bca03b952 browser=:firefox>
  7. irb(main):004:0>   driver.navigate.to 'http://google.com'
  8. => ""
  9. irb(main):005:0>   puts driver.title
  10. Google
  11. => nil
  12. irb(main):006:0> driver.quit
  13. => nil
  14. irb(main):007:0> Headless.ly do
  15. irb(main):008:1*   driver = Selenium::WebDriver.for :firefox
  16. irb(main):009:1>   driver.navigate.to 'http://google.com'
  17. irb(main):010:1>   puts driver.title
  18. irb(main):011:1> end
  19. Google
  20. => nil
  21. irb(main):012:0> driver.quit
  22. Errno::ECONNREFUSED: Connection refused - connect(2)

12行目でdriver.quitしているのは、サンプルプログラムだとFirefoxインスタンスが残っていたからなのですが、同じことを21行目で行おうとしてエラーになっています。
これは単にブロック(スコープ)の外だからなのですが、xvfbの中だとブラウザが残っているかどうかわかりにくいので、ensure節などで確実に閉じる必要があります。

特にCUIのビルドサーバ上で実行する際など、メモリの無駄遣いになりますのでご注意を。

最後になりますが、当然ながらwindowsでは使えないようです。
macは。。未調査です。

タグ: , , ,

TrackBack