Selenide～Javaで超簡単・簡潔にUIテストを書く～

## はじめに
Selenideを触ってみたら簡単なUIテストがサクッと書けて感動したので、使い方をまとめました。

## Selenideとは
SelenideはUIテストのためのSeleniumラッパーです。

> Selenide: concise UI tests in Java
> http://selenide.org/

Java界隈でUIテストといえばSelenium(WebDriver)が有名で、大変優れたツールです。ですが、SeleniumはUIテストのためのツールではなく、ブラウザ操作のためのツールです[^1]。

[^1]: ・・・と、Selenideの公式ページが申しております。http://selenide.org/documentation/selenide-vs-selenium.html

一方、Selenideは初めからUIテストのために設計されていて、UIテストを「スラスラ」書いて実行できます。ブラウザの閉じ方とか、タイムアウトとか、StaleElement Exceptionsの扱い方などを考える必要はなく、ユーザはテストに集中できるのが売りです。


## Three simple things
SelenideでのUIテストは以下のThree simple thingsが全てです。

1. ページを開く
1. $(find element).アクションを起こす()
1. $(find element).状態をチェックする()

例えばこんな具合です。

~~~java
open("/login");       // ページを開いて
$("#submit").click(); // #submitをクリックして
$(".message").shouldHave(text("Hello")); // .messageがHellowというテキストを持っているかチェック
~~~


## 試す
とにかく試してみましょう。

### 超簡単な前準備
まず、（Firefoxを使う場合）前準備はたったひとつ。 selenide.jarをプロジェクトに導入するだけです。
<small>※junitへの依存性を記述していますが、今回のサンプルのために入れてるだけで必須ではありません。</small>

#### Maven利用者なら
`pom.xml`に以下を追加してください。

~~~xml
<dependency>
    <groupId>com.codeborne</groupId>
    <artifactId>selenide</artifactId>
    <version>5.1.0</version>
    <scope>test</scope>
</dependency>
<dependency>
    <groupId>junit</groupId>
    <artifactId>junit</artifactId>
    <version>4.12</version>
</dependency>
~~~

#### ivy利用者なら
`ivy.xml`に以下の行を追加してください。

~~~xml
<ivy-module>
  <dependencies>
    <dependency org="com.codeborne" name="selenide" rev="5.1.0"/>
    <dependency org="junit" name="junit" rev="4.12"/>
  </dependencies>
</ivy-module>
~~~

#### Gradle利用者なら
`build.gradle`に以下の行を追加してください。

~~~gradle
dependencies {
  testCompile 'com.codeborne:selenide:5.1.0'
  testCompile 'junit:junit:4.12'
}
~~~

これだけで準備完了です！

### テストを書く

~~~java
import static com.codeborne.selenide.Condition.*;
import static com.codeborne.selenide.Selenide.*;
import static com.codeborne.selenide.Selectors.*;

import org.junit.Test;
import org.openqa.selenium.By;

public class SelenideTest {

	@Test
	public void test() {
		// Googleトップページ
		// "selenide"を検索
		open("https://www.google.co.jp/");
		$("input[type=text]").val("selenide").pressEnter();

		// 検索ページ
		// Selenideの公式ページをクリック
		$(byText("Selenide: concise UI tests in Java")).click();

		// Selenide公式ページ
		// 「What is Selenide?」という文言があることを確認
		$("body").shouldHave(text("What is Selenide?"));
	}

}
~~~

テストコードの説明は・・・読めばわかりますね。上述のThree simple thingsに沿って記述してあります。タイムアウトや例外のハンドリングといった「ノイズ」が一切なく、非常に簡潔でわかりやすく記述できてるのが分かると思います。

さすが、「膨大なドキュメントを読まなくても使える」を自負するだけあります。

### テストを実行する
特別なことを考慮する必要はありません。いつも通りテストを実行してください。

実行すると、

1. ブラウザが勝手に立ち上がり
1. グーグルで`selenide`を検索してselenideの公式ページをクリックして
1. (これは見えないけど) 「What is Selenide?」があることをチェック

というテストが実行されます。

> ![テスト実行の様子](https://qiita-image-store.s3.amazonaws.com/0/29682/9a7a4839-0138-1bff-e8b2-821c90b3c4e6.gif)

ちなみに`$("body").shouldHave(text("What is Selenide?"));`のtextを存在しない文言に変えるとテストは失敗します。

> ![テスト失敗.png](https://qiita-image-store.s3.amazonaws.com/0/29682/dbe065d3-3397-ff81-578b-97f6749bf28a.png)


今回はjUnitを使って書きましたが、別に特定のテストランナーに依存しません。TestNGでも何でも構いません。`public static void main(String[] args)`に記述したっていいです。

なんて簡単なんだろう。

## よく使うAPI
サンプルを見て大体雰囲気はつかめたと思いますが、いくつか、代表的なものを補足します。

### ページを開く
`com.codeborne.selenide.Selenide.open`メソッドでブラウザを開きます。

`com.codeborne.selenide.Selenide.*`をstaticインポートして使うと良いでしょう。

### 要素を選択する
#### ロケータ

要素を選択するにはjqueryライクの`$(selector)`ロケータ、もしくは`$$(selector)`ロケータを使います。

* $(selector)  : 単一のSelenideElementを返します。
* $$(selector) : SelenideElementのコレクションを返します。

`com.codeborne.selenide.Selenide`クラス内のstaticメソッドなので、同クラスをstaticインポートして使います。

また、細かいことですが、ロケータを定義した時点ではDOMの検索は実行されません。アクションを起こしたとき、状態をチェックした時に初めてDOMの検索が実行されます。

#### セレクタ
セレクタにはCSSセレクタや、Seleniumの[By](https://selenium.googlecode.com/git/docs/api/java/org/openqa/selenium/By.html)セレクタなどが使えます。

~~~java
$("input[name=username]");         // name属性が「username」であるinput要素を検索
$$(By.linkText("こちら")).first(); // テキストが「こちら」であるリンクの最初の要素を取得
~~~

他にも[com.codeborne.selenide.Selectors](http://selenide.org/javadoc/3.5.1/com/codeborne/selenide/Selectors.html)クラスに定義されたセレクタが利用できます。


### アクションをおこす
`SelenideElement`のインスタンス経由で、クリックなどのアクションを起こすことができます。
代表的なものは以下があります。挙動はメソッド名から想像できると思います。

~~~java
$("#hoge").click();
$("#hoge").doubleClick();
$("#hoge").pressEnter(String);
$("#hoge").selectOption(String text);
$("#hoge").selectOptionByValue(String value);
$("#hoge").setValue(String);
$("#hoge").val(String);
$("#hoge").append(String);
~~~

上述した通り、`$("hoge")`の時点ではDOMの検索は実行されず、アクションメソッドを呼び出した時点で検索が実行されます。

### 状態をチェックする(アサーション)
状態のチェックも`SelenideElement`のインスタンス経由で行います。以下のメソッドが使えます。

~~~java
$("#hoge").should(Condition);
$("#hoge").shouldBe(Condition);
$("#hoge").shouldHave(Condition);
$("#hoge").shouldNot(Condition);
$("#hoge").shouldNotBe(Condition);
$("#hoge").shouldNotHave(Condition);
~~~

`Condition`は状態を表すオブジェクトで、[`com.codeborne.selenide.Condition`](http://selenide.org/javadoc/3.5.1/com/codeborne/selenide/Condition.html)クラスにヘルパメソッド・フィールドが定義されています。

* readonly
* attribute(String)
* name
* value
* type
* id
* empty
* options
* cssClass(String)
* focused
* enabled
* disabled
* selected
* matchText(String regex)
* text(String substring)
* exactText(String wholeText)
* textCaseSensitive(String substring)
* exactTextCaseSensitive(String wholeText)

例えば、以下のように利用します。

~~~java
import static com.codeborne.selenide.Selenide.*;
import static com.codeborne.selenide.Condition.*;
・・・
$("#hoge").shouldBe(focused);            // #hogeがフォーカスされた状態であること
$("#hoge").shouldHave(cssClass("fuga")); // #hogeに"fuga"というクラスが付与されていること
~~~

### 設定
[`com.codeborne.selenide.Configuration`](http://selenide.org/javadoc/3.5.1/com/codeborne/selenide/Configuration.html)クラスのクラスフィールドを変更することで、テストの設定を変えることができます。

~~~java:設定変更の一例
@BeforeClass
public static void beforeClass() {
	// タイムアウトの時間を5000ミリ秒にする(デフォルト:4000ミリ秒)
	Configuration.timeout = 5000;
	
	// ベースURLを変更する (デフォルト:http://localhost:8080)
	Configuration.baseUrl = "http://localhost:8080/app";
	
	// レポート用のディレクトリを変更する
	Configuration.reportsFolder = "/var/selenide";
	
	// テスト実行後にブラウザを開いたままにする
	Configuration.holdBrowserOpen = true;
}
~~~

実行するブラウザの変更もここから行えます。
ただし、IEやChromeを利用する場合は別途ドライバの導入とプロパティの設定が必要になります[^2]。

[^2]: 私の環境では当初なぜがchromeがうまく動かなくて、chromeを再インストールしたら動くようになりました。

~~~java
// デフォルトではFilrefox。特にプロパティの設定は不要
Configuration.browser = WebDriverRunner.FIREFOX;

// Chrome(要ドライバー)
Configuration.browser = WebDriverRunner.CHROME;
System.setProperty("webdriver.chrome.driver", "[パス]/chromedriver.exe");

// IE(要ドライバー)
Configuration.browser = WebDriverRunner.INTERNET_EXPLORER;
System.setProperty("webdriver.ie.driver", "[パス]/IEDriverServer.exe");
~~~

ドライバはこちらから
* IE: http://docs.seleniumhq.org/download/
* Chrome: https://sites.google.com/a/chromium.org/chromedriver/downloads


## その他
その他、PageObjectパターンというお作法があるようなのですが、そっちはまだ勉強してません・・・勉強したらまた記事を書こうかと思います。→[書きました](http://qiita.com/tatesuke/items/0bac60172e7cfd12aeb1)

ただ、ページオブジェクトパターンを知らなくても簡単なテストならさくっと書けそうですね！