Java実装のWebDriverラッパー Selenideを使う

# Selenideとは

[Selenide](http://selenide.org/)とはWebDriverのJavaラッパーの一つで、同じJavaで実装された[FluentLenium](http://fluentlenium.org/)やGroovyでの[Geb](http://www.gebish.org/)に相当するものです。
WebDriverはプログラムからブラウザを操作するAPIを提供していますが、基本的に低レベルAPIで自動テストを記述するには大量のコードが必要となります。SelenideはWebDriverを基盤としながら高水準APIを提供することでWebDriverを素で使うよりも容易にかつ記述量を抑えてテストを記述することができます。

# Selenideの特徴

以下ではSelenideの特徴を述べていきます。これらの中には上記であげたFluentLeniumやGebでも提供されているものもあります。(すべてがSelenide固有ということではありません)

## 要素取得・チェックなどの高水準APIの提供

他の類似プロダクトでもありますが、HTML上の要素取得や状態判定、Assertionの高水準APIが提供されます。Selenideの[サンプル](http://selenide.org/quick-start.html)にあるように以下のようき書きます。

``` Java
import static com.codeborne.selenide.Selenide.*;
import static com.codeborne.selenide.Condition.*;

@Test
public void userCanLoginByUsername() {
  open("/login");
  $(By.name("user.name")).setValue("johny");
  $("#submit").click();
  $(".loading_progress").should(disappear); // Waits until element disappears
  $("#username").shouldHave(text("Hello, Johny!")); // Waits until element gets text
}
```

普段Javaでコードを書いていると色々驚くことがあります。このサンプルコードを見ただけでも以下のようなことに気がつくと思います。

* 初期処理を行っていないのに動く。
* 「$」でセレクタが始まる。
* セレクタがjQuery風につかえる。
* クリックがclick()メソッドで済む。
* shuold()やshouldHaveでAssertionが行える。
* text("hoge") でテキストの状態を判定できる。

## static import を利用したDSLの提供

前述の高水準APIと重複する部分がありますが、Java5から導入されたstatic importを利用したDSL風の記述が行えるようになっています。

先のサンプルコードでも出ていた「$」というメソッドは[com.codeborne.selenide.Selenide](http://selenide.org/javadoc/4.0/com/codeborne/selenide/Selenide.html)というクラスのstatic methodですが、テストコードでstatic importを行うことであたかもjQueryを使っているかのように自然にセレクタを記述できます。

他にも[com.codeborne.selenide.Condition](http://selenide.org/javadoc/4.0/com/codeborne/selenide/Condition.html)という状態を表現するクラスがあり、static field として以下のようなものが定義されています。

* appear 
* disappear
* enable
* disabled
* empty
* checked

サンプルコードの

``` Java
$(".loading_progress").should(disappear);
```
で使われている`disappear`がまさにこの`Condition.disappear`であり、static importを使うことで自然言語(この場合は英語ですね)に感じで記述することができます。

他にも`$`メソッドと組み合わせて使う検索条件である[com.codeborne.selenide.Selectors](http://selenide.org/javadoc/4.0/com/codeborne/selenide/Selectors.html)を使うことで以下のように記述できます。

``` Java
$(byId("employeeTree")).click()
```
ここでの`byId`は`Selectors#byId`をstatic import した結果です。

## Page Objectパターンを標準でサポート
Selenideは標準でPage Objectパターンをサポートしています。
といっても`page(PageClass.class)`や`open("http://example.com/hoge.html", HogePage.class)`で指定したPageObjectを生成して戻してくれるくらいなのですが。ただ、ちょっとしたことでもこうした仕組みと、公式で提供している[PageObjectを使用したサンプルコード](https://github.com/selenide-examples)をみることで理解の促進につながるかと思います。

## わかりやすく強力なAssertion

WebDriverはブラウザをプログラムで動かす仕組みを提供していますが、テストフレームワークではありません。なので、Assertionは各言語、各xUnitなどのUnitTestフレームワークに依存します。Javaで書く場合はメジャーなところでJUnit4かTestNGですが、どちらにしてもWebDriverを考慮しているわけではありません。  

Selenideはテストフレームワークであり、便利なAssertionも提供されています。最初のサンプルコードであった

``` Java
$(".loading_progress").should(disappear);
```
に現れる`should(Condition)`がまさしくそうです。これは、セレクタで取得した要素が引数[Condition](http://selenide.org/javadoc/4.0/com/codeborne/selenide/Condition.html)で指定した状態であることを検査するメソッドです。要素から状態をとってそれを比較、とすることなくシンプルにかつ可読性高く記述できます。

この`should(Condtion)` に代表されるAssertionは全て単一要素をあらわす[SelenideElement](http://selenide.org/javadoc/4.0/com/codeborne/selenide/SelenideElement.html)、複数の要素を表す[ElementsCollection](http://selenide.org/javadoc/4.0/com/codeborne/selenide/ElementsCollection.html)で定義されています。
これらは`$`などのSelenide固有のセレクタの戻り値ですので自然に記述することができます。


## IDEとの親和性

[公式サイトのドキュメントページ](http://selenide.org/documentation.html#use-the-power-of-ide)に説明がありますが、API設計がIDEによるコード保管を前提に設計されています。SelenideのAPI戻り値はほぼ全てSelenideのObjectで、それらには適切な名称でメソッドが定義されているため、IDEを使ってコードを書く際Selenideの戻り値に`.`を打つことでドキュメントをほとんど見ずに書くことができます。


## 柔軟な「待ち」とAjax対応

ブラウザベースの自動テストを記述しているとどうしても避けらないのが、テストで行いたい操作内容そのものと直接関係のない「待ち」処理です。Ajaxなどの非同期処理やそのたJavaScriptを用いたイベント処理で、処理が完了したあとに何か操作をしたいのにテストコード上では明示的に待ち処理が必要になります。また待ちも時間指定ではその時の実行環境(テストを実行しているPCの性能やネットワーク環境)に依存するため、なかなかテストが安定せず成功したり失敗したりということがあります。

Selenideではこれらの問題を緩和するために全ての処理に待ち処理が入るようになっています。デフォルト待ち時間は4秒と設定されていて、0.1秒毎にポーリングされます。この時間は変更可能です。  
また全体での待ち処理とは別に、時間だけでなく状態による待ちも指定することができます。

``` Java
$("#submit").waitUntil(enable, 1000).click()
```

のように記述できる以下のメソッドが提供されます。

* waitUntil
* waitWhile

第一引数はConditon、第二引数は待ち時間で、待ち時間をを超えて指定した状態にならない・状態から変化した場合に例外が発生する仕組みです。このメソッドの戻り値も`SelenideElement`なので、引き続き要素に対する操作を記述できます。
メソッド名と合わせて可読性の良さがお分かりいただけるのではないでしょうか。

## テスト向けの便利な機能
ブラウザを用いたテストを実装するうえで便利な機能を提供しています。その一例として、自動テスト中に失敗した場合に自動的にブラウザ画面をPNG形式でキャプチャし、またその際のHTMLも保存する仕組みが備わっています。  
これはSelenideがデフォルトで行うもので、利用者は特別な設定やAPI呼び出しなどは一切必要としません。もちろん、自分でキャプチャを取りたい場合は任意でAPI呼び出しを行うことでも可能です。

## 継承不要
同じJava向けWebDriverラッパーであるFluentLeniumとの比較で個人的に一番大きな点だと思っているのが、この継承不要であるということです。
Selenideは一切継承を必要とせず、必要な時にSelenideが提供しているstatic methodを呼び出すことでその機能を利用できます。そのため、自分たちのテスト実装(基盤やフレームワークを含む)について継承関係を自由に作ることができます。
自動テスト基盤で継承はさほど使うものではないかもしれませんが、私はPageObjectの親クラスを作成させるなどしているのでこの点は大きなメリットを感じています。
もっともこの制限はJava8から導入されたinterfaceのdefault methodで大部分緩和されていますが。

# 終わりに

ざっとSelenideの特徴的な部分について説明してきましたがいかがだったでしょうか。E2Eの自動テストはやりたいけれど準備が大変そう、覚えるAPIが多そうと感じていた人が多いかもしれませんが、Selenideを使うことでかなりの部分緩和できるのではないでしょうか。

私は仕事でSelenideを使ってE2Eテストの基盤(PageObjectおよび共通部分)を作りましたが、それまで聞きかじりでWebDriverを知っていた状態から一週間で大体困らずにSelenideを使いテストをかけるようになりました。

ぜひみなさんもSelenideを使って楽しくブラウザベースのE2Eテストを書いていきましょう。
次回以降はSelenideを使ったテスト基盤(Page Object＋α)について、実際に仕事で書いた経験をもとに説明していきます。