GhostDriverでWebアプリケーションのテストを高速化する

f:id:hutyao:20130105043408p:plain

Seleniumを使ったテストは遅いという話をよく聞きます。理由として考えられるのは以下の3つです。

  • テスト対象のWebアプリケーションが遅いため
  • 遅いテストを書いているため
  • 実際にWebブラウザを動かしているため

1つ目と2つ目は、環境やコードの書き方を変えることで改善可能です。しかし、3つ目はどうでしょうか。実際のブラウザを高速化することなど、そのブラウザの開発者でもない限り不可能です。となると、できることは、可能な限り高速なブラウザを選択することです。

PhantomJSはヘッドレスブラウザです。ヘッドレスブラウザというのはGUIのないブラウザのことで、GUIの描画処理を行わない分、通常のブラウザに比べて高速に動作するという特徴があります。そのため、時間が掛かりがちなWebアプリケーションのGUIテストを高速化するためのソリューションとして注目されています。また、マルチプラットフォームであり、CUIのみのOSでもGUIテストを行えるという利点もあります。

GhostDriverは、そのPhantomJSをバックエンドとするWebDriver Wire Protocolの実装です。Selenium WebDriver APIを用いて、PhantomJSでのテストを行うことを目的として、Ivan De Marino氏によって作られました。

今回は、このGhostDriverの使い方について書きます。

準備

PhantomJSのインストール

PhantomJS 1.8以降には、GhostDriverが標準でバンドルされています。1.8以降のバージョンのPhantomJSをインストールすれば、GhostDriverを別途インストールする必要はありません。 PhantomJSのインストール方法は、OSごとに方法が異なります。詳細については以下のページを参照してください。

PhantomJS: Download and Install

Seleniumのダウンロード

Seleniumには、バージョン2.27.0からGhostDriverのサポートが追加されました。GhostDriverを利用するための実装が、各言語バインディングに追加されています。

今回はJavaでGhostDriverを利用する例を示します。JavaでGhostDriverを利用するためには、まず以下のページから最新バージョンのselenium-java-2.x.0.zipをダウンロードします。

Downloads - selenium - Browser automation framework - Google Project Hosting

ダウンロードしたものを解凍し、selenium-java-2.x.0.jarと、libsフォルダ以下のJARファイルを全てクラスパスに追加します。

SeleniumのJARファイルはMavenを利用してダウンロードすることもできますが、2012年1月7日現在、PhantomJSを動かすためのクラスを含むJARファイルがMavenのセントラルリポジトリ上で提供されていないため、そのJARファイルのみがダウンロードされません。どうしても利用したい場合、先のZIPファイルをダウンロードし、その中に含まれるphantomjsdriver-1.0.0.jarを、自身で依存関係に追加する必要があります。この問題についてはgithubのIssueに挙がっているので、いずれ解決されると思われます。

GhostDriver Selenium bindings should be available via Maven · Issue #142 · detro/ghostdriver

1/12 追記

GhostDriverがMavenリポジトリに登録されたようです。pom.xmlに以下の記述を追加することで利用可能になります。

<dependency>
  <groupId>com.github.detro.ghostdriver</groupId>
  <artifactId>phantomjsdriver</artifactId>
  <version>1.0.1</version>
</dependency>

使い方

SeleniumJavaバインディングでは、PhantomJSDriverというクラスを利用してPhantomJSを操作します。"/path/to/phantomjs"の部分は、PhantomJSのバイナリファイルへのパスを指定します。

DesiredCapabilities caps = new DesiredCapabilities();
caps.setCapability(
        PhantomJSDriverService.PHANTOMJS_EXECUTABLE_PATH_PROPERTY,
        "/path/to/phantomjs"
);
WebDriver driver = new PhantomJSDriver(caps);
driver.get("http://www.google.com");
WebElement searchBox = driver.findElement(By.name("q"));
searchBox.sendKeys("phantomjs");
searchBox.submit();
System.out.println(driver.getTitle());
driver.quit();

バイナリファイルのあるディレクトリに対してパスが通っていれば、バイナリファイルへのパスを指定する必要はありませんが、PhantomJSDriverには引数を受け取らないコンストラクタがないため、空のDesiredCapabilitiesを渡すことになります。

上記のコードを実行すると、コンソール上に「PhantomJS is launching GhostDriver... Ghost Driver running on port xxxxx」というメッセージが出力された後、「phantomjs - Google 検索」という文字が出力されます。画面上には一切表示されませんが、バックグラウンドでPhantomJSが起動し、HTTPリクエスト/レスポンスの送受信、HTMLの解析、JavaScriptの実行などが行われていることがわかります。

スクリーンキャプチャの取得

PhantomJSはGUIのないブラウザですが、Webページのキャプチャを取得することができます。GhostDriverを利用する場合も、以下のようにしてキャプチャの取得が可能です。

WebDriver driver = new PhantomJSDriver(caps);
driver.get("http://www.google.com");
File screenshot = ( (TakesScreenshot) driver).getScreenshotAs(OutputType.FILE);
Files.copy(screenshot, new File("/path/to/phantomjs-screencapture.png"));

Selenium Grid上での使用

Selenium Grid上でGhostDriverを使用する場合は、以下のようなコマンドラインオプションを付加してPhantomJSを起動し、PhantomJSをGridのnodeとしてhubに対して登録します。

$ java -jar /path/to/selenium-server-standalone-2.x.0.jar -role hub
$ phantomjs --webdriver=8080 --webdriver-selenium-grid-hub=http://127.0.0.1:4444/

nodeを登録したhubに対して、RemoteWebDriverでブラウザを指定して接続することで、Grid上でPhantomJSを使ったテストを動かすことができます。

WebDriver driver = new RemoteWebDriver(
        new URL("http://127.0.0.1:4444/wd/hub"),
        DesiredCapabilities.phantomjs());

実行速度の比較

GhostDriverを使ったテストにおいて、実ブラウザと比べて実際にどれほどの実行速度の差があるのか、簡易的に計測してみました。以下のグラフは、FirefoxDriver、ChromeDriver、PhantomJSDriverの三つのドライバを使って、ブラウザの起動、操作、終了までを20回ほど繰り返し、それぞれ掛かった時間の合計をまとめたものです。

f:id:hutyao:20130104223401p:plain

ブラウザの操作に関しては実ブラウザと比べるとなかなかの速さです。やはり画面の描画がないのが大きいようです。逆に、ブラウザの起動と終了に関しては実ブラウザと大差はありませんでした。この点は意外です。

まとめ

これまで、Seleniumを使ってヘッドレスブラウザでのテストを行う場合、選択肢はHtmlUnitのみでした。しかし、HtmlUnitはあくまで実ブラウザをシミュレートしたものであって、実ブラウザとの挙動の差があるのが難でした。しかしその点、PhantomJSはGoogle Chromeでも使われているレンダリングエンジンであるWebKitをベースとしており、ほとんど実ブラウザと同じといっても良いレベルです。

ただ、GhostDriverを一通り使ってみた感じでは、実ブラウザに比べてまだまだ不安定な部分があるように思いました。存在するはずの要素を見つけられない、リンクをクリックしてもうまく次のページへ遷移しないなど、同じコードでも、他のドライバの使用時には起こらないようなエラーが起こることが多々ありました。また、Wire Protocolに関しても未実装のコマンドがまだ多くあるようです

GhostDriverはまだまだ実用レベルではありませんが、高再現性かつ高速という点では大きなアドバンテージを持っています。特に、テストケースが増えるとSlow Tests問題を起こしやすいGUIレベルの自動テストでは、高速にテストを実行するための基盤は必須です。そして、それは実ブラウザを使っていては限界があります。そういった場面では、やはりヘッドレスブラウザが活躍してきます。この先、安定性が向上すれば、GhostDriverはWebアプリケーションを包括的にテストする際の有用な選択肢となってくるのではないでしょうか。

参考