FluentLenium入門

# はじめに
[FluentLenium](https://github.com/FluentLenium/FluentLenium) はブラウザの自動操作ツールであるSeleniumをより使いやすくしたフレームワークです。FluentLeniumによりブラウザの自動制御プログラムを分かりやすく記述することができます。FluentLeniumは主にブラウザの自動テストに用いられます。

以下では、FluentLeniumの使い方を簡単にまとめてみます。

# 初期設定
まずは、FluentLeniumを設定します。ここでは、mavenを利用します。

## Mavenで空のプロジェクトを作る
GroupIdなどは適当に変えてください。

```shell-session
$mvn archetype:generate -DgroupId=sample -DartifactId=fluentlenium-sample -DarchetypeArtifactId=maven-archetype-quickstart -DinteractiveMode=false
```

## pom.xmlの依存にFluentLeniumを追加する
こんな感じです。

```xml
  	<dependency>
    	<groupId>org.fluentlenium</groupId>
    	<artifactId>fluentlenium-core</artifactId>
    	<version>0.10.3</version>
    	<scope>test</scope>
	</dependency>
```

# 基本的な使い方
ブラウザの自動制御のみを行うためのもっとも基本となる使い方です。

## Fluentクラスを継承したクラスを作成する
org.fluentlenium.core.Fluentクラスは、SeleniumのWebDriverをラップし、より使いやすくしたメソッドを提供します。ブラウザの自動制御を行うには、このFluentクラスを継承したクラスを作成します。

```java
package sample;

import org.fluentlenium.core.Fluent;
import org.openqa.selenium.firefox.FirefoxDriver;

//Fluentクラスを継承する
public class FluentSample extends Fluent {	
	
	public void Yahooで検索条件を入力して検索する() {		
		//WebDriverを指定して初期処理（この例ではFireFox用のWebDriver）
		//ブラウザに応じたWebDriverを設定する
		//この初期処理により、指定したWebDriverのブラウザが立ち上がる
		initFluent(new FirefoxDriver());
		
		//Yahooのサイトに遷移する
		goTo("http://www.yahoo.co.jp");

		//検索入力用のテキストボックスに条件を設定
		fill("#srchtxt").with("Qiita");

		//検索ボタンをクリック
		click("#srchbtn");		
	}
	
	//上記のサンプルを実行
	public static void main(String[] args) {
		FluentSample sample = new FluentSample();
		sample.Yahooで検索条件を入力して検索する();
	}
}

```
上記の例のように、Fluentクラスは、CSSを利用してHTML要素を参照したり値を設定する豊富なユーティリティメソッドを提供しています。

# JUnitテストの作り方
ブラウザの自動テストは、JUnitを利用します。（FluentLeniumはTestNGも利用することができます）

## pom.xmlにテストフレームワークの依存を追加する
JUnitとAssertJを依存に追加します。

```xml
    <dependency>
      <groupId>junit</groupId>
      <artifactId>junit</artifactId>
      <version>4.12</version>
      <scope>test</scope>
　  </dependency>
　  <dependency>
    	<groupId>org.fluentlenium</groupId>
    	<artifactId>fluentlenium-assertj</artifactId>
    	<version>0.10.3</version>
    	<scope>test</scope>
　  </dependency>
```
[AssertJ](http://joel-costigliola.github.io/assertj/)はFluentLeniumが推奨しているアサート用のフレームワークです。JUnitのアサートのみを使う場合は、AssertJの依存は必要ありません。

## FluentTestを継承してテストクラスを作る
FluentLeniumでは、org.fluentlenium.adapter.FluentTestを継承してテストクラスを作成します。この点を除けば、通常のJUnitのテストクラスと変わりません。

```java
package sample;

import static org.assertj.core.api.Assertions.assertThat;

import org.fluentlenium.adapter.FluentTest;
import org.junit.Test;

//JUnitのテストクラスはFluentTestクラスを継承して作る
public class YahooTest extends FluentTest {	
	
	@Test
	public void 条件を入力して検索すると検索結果画面のタイトルは条件を含む() {
		//検索条件を入力して検索
		goTo("http://www.yahoo.co.jp");
		fill("#srchtxt").with("Qiita");
		click("#srchbtn");
		
		//タイトルは検索結果を含むこと
		assertThat(title()).contains("Qiita");
	}
	
	@Test
	public void 検索結果の次ページに遷移するとページ数を表示する() {
		//検索条件を入力して検索
		goTo("http://www.yahoo.co.jp");
		fill("#srchtxt").with("Qiita");
		click("#srchbtn");
		
		//次へ遷移
		find("#Sp1").find("span").get(1).find("a").click();		
		
		//ページが表示されること
		assertThat(find(".resultNum span").get(2).getText()).isEqualTo("2");
	}
}
```
FluentLeniumでは、Fluentという名前が示すとおり、流れるインターフェースを利用してHTMLの操作を行いテストを実施します。
上記の例では、AssertJを利用してアサートを行っています。

## ブラウザのライフサイクルを設定する
テストクラスに@SharedDriverを指定することで、ブラウザのライフサイクルを変更できます。指定しない場合は、テストメソッドごとに新しいブラウザウィンドウが立ち上がります。
これをテストクラス単位に変更したい場合、以下のようにアノテーションを指定します。

```java
//SharedDriver.SharedType.PER_CLASSを指定すると、テストクラス内の全てのテストメソッドは同じウィンドウを共有する
@SharedDriver(type = SharedDriver.SharedType.PER_CLASS)
public class YahooTest extends FluentTest {
//...
}
```
指定できるタイプは以下の３つです。

|タイプ|意味|
|:---|:---|
|SharedDriver.SharedType.ONCE|全てのテストで同じウィンドウを使う||
|SharedDriver.SharedType.PER_CLASS|テストクラス単位に同じウィンドウを使う||
|SharedDriver.SharedType.PER_METHOD|テストメソッド単位に別のウィンドウを使う|指定しない場合のデフォルトです|



# ページオブジェクトパターン
上記のテストクラスでは、HTML要素を参照するためのCSSセレクタが直接現れており、HTMLと密結合したプログラムとなっています。このため、以下のような問題を抱えています。

* CSSセレクタを見ただけでは何を操作しているのかが分かりにくい。
* idなどのHTML構造が変わった場合にテストクラスを修正する必要がある。

このような問題を解決する手法として、[ページオブジェクトパターン](https://code.google.com/p/selenium/wiki/PageObjects)を利用できます。ページオブジェクトパターンでは、各画面単位にページと呼ばれるクラスを作成し、画面に対する操作はページクラスで抽象化し、CSSセレクタなどのHTMLへのアクセス方法はページクラス内に隠蔽します。HTMLではなく、ページクラスを操作することで、可読性の高いテストを作成することができます。

## 各画面に対応するページクラスを作る
ページクラスは、FluentPageを継承して作成します。

まずは、検索結果画面に対応するページクラスを作成します。

```java
//検索結果画面のPage
class ResultPage extends FluentPage {
	
	public ResultPage 次ページを表示する() {
		find("#Sp1").find("span").get(1).find("a").click();
		return this;
	}
	public String タイトル() {
		return super.title();
	}
	public String ページ数() {
		return find(".resultNum span").get(2).getText();
	}
}
```
上記の「次ページを表示する」メソッドのように、voidではなくページオブジェクト自身を返すのが基本的です。これにより、FluentLeniumの考え方である流れるようなインターフェースを実現できます。

次に、トップ画面のページを作成します。

```java
//Yahooのトップ画面のページ
public class YahooPage extends FluentPage {
	
	@Override
	public String getUrl() {
		return "http://www.yahoo.co.jp";
	}

	public YahooPage 検索条件を入力する(String query) {
		fill("#srchtxt").with(query);
		return this;			
	}
	
	public ResultPage 検索する() {
		click("#srchbtn");
		return createPage(ResultPage.class); 
	}
}
```

トップ画面は最初に遷移する画面であるので、```getUrl``` メソッドをオーバーライドして画面のURLを指定します。
「検索する」メソッドが検索結果画面のページオブジェクトをリターンしているところに注目してください。ページオブジェクトから別のページオブジェクトをリターンすることで、画面遷移を簡単に表現できます。ページオブジェクトは ```createPage``` で作成できます。

## テストクラス
上記のページオブジェクトを利用したテストクラスです。ページオブジェクトを利用する前のテストクラスと比較してみてください。

```java
package sample;

import static org.assertj.core.api.Assertions.assertThat;

import org.fluentlenium.adapter.FluentTest;
import org.fluentlenium.adapter.util.SharedDriver;
import org.fluentlenium.core.annotation.Page;
import org.junit.Test;

//PageObjectパターンによるテストクラス
@SharedDriver(type = SharedDriver.SharedType.PER_CLASS)
public class YahooTestUsingPage extends FluentTest {
	@Page
	YahooPage yahooPage;	
	
	@Test
	public void 条件を入力して検索すると検索結果画面のタイトルは条件を含む() {
		goTo(yahooPage);
		
		ResultPage resultPage = yahooPage.検索条件を入力する("Qiita").検索する();		 		
		
		//タイトルは検索結果を含むこと
		assertThat(resultPage.タイトル()).contains("Qiita");
	}
	
	@Test
	public void 検索結果の次ページに遷移するとページ数を表示する() {
		yahooPage = goTo(yahooPage);
		
		ResultPage resultPage = yahooPage.検索条件を入力する("Qiita").検索する().次ページを表示する();
		
		//ページが表示されること
		assertThat(resultPage.ページ数()).isEqualTo("2");
	}
}
```
上記のように、ページオブジェクトと流れるインターフェースを利用することで、非常に読みやすいテストになります。