FluentLeniumでSeleniumの使いにくさを解消。
今回は、Selenium2 WebDriverでの自動テストの開発をさらに楽にしてくれるラッパーフレームワーク FluentLeniumを紹介します。 ラッパーなので、純粋なSelenium2 WebDriverに比べると、制限されることや動作スピードが落ちたりはしますが、名前の通りさくさく流れるように開発することができるようになります。
FluentLeniumの特徴
jQueryライクなセレクタ
jQueryのセレクタ記法をそのまま利用した操作対象の要素の特定が可能です。 jQueryを普段利用しているフロントエンドの開発者なら、簡単にテストコードを書くことができます。
- id属性で指定
$("#login-button").click();
- name属性で指定
$("input[name=password]").text("foo");
- class属性で指定した要素の下の階層にある特定のタグ要素を操作
$(".sample-class").find("span").click();
流れるインターフェース
いわゆる「流れるインターフェース」が意識されたAPIとして設計されているので、各APIの細かい部分を覚えていなくてもIDEのコード補完で直感的に開発可能です。
// id goTo("/login").await().until("#user_cd").areDisplayed() .find("#user_cd").text("foo");
セットアップ
Mavenプロジェクトの場合、下記のdependencyをpom.xmlに追加します。 注意点としては、これらのプロジェクトの依存関係にjunitは入っていないので、junitのアノテーションを利用するにはjunit自体も依存関係に追加する必要があります。
<dependency> <groupId>org.fluentlenium</groupId> <artifactId>fluentlenium-core</artifactId> <version>0.10.3</version> </dependency> <dependency> <groupId>org.fluentlenium</groupId> <artifactId>fluentlenium-assertj</artifactId> <version>0.10.3</version> </dependency> <dependency> <groupId>org.fluentlenium</groupId> <artifactId>fluentlenium-testng</artifactId> <version>0.10.3</version> </dependency>
基本的な利用法
org.fluentlenium.adapter.FluentTestの継承
テストコードとして記述していくには、org.fluentlenium.adapter.FluentTestクラスを継承します。
import org.fluentlenium.adapter.FluentTest; public class SampleFluentLenium extends FluentTest { ・ ・ ・ }
ドライバーの指定
デフォルトではFirefoxDriverが読み込まれますが、getDefaultDriverをオーバーライドすることで独自にWebDriverのインスタンスを作ることができます。
@Override public WebDriver getDefaultDriver() { System.setProperty("webdriver.chrome.driver", "/Users/xxxx/Downloads/chromedriver"); return new ChromeDriver(); }
JavaDoc FluentTest#getDefaultDriver())
ページ移動
FluentTest#goToにてアクセスしたいURLを指定します。 テストケースにおいてベースとなるURLが決まっている場合は、FluentTest#withDefaultUrlにベースURLを指定し、FluentTest#goToでは相対URLとして記述します。
- 絶対URLでのアクセス
goTo("https://www.google.co.jp/");
- ベースURLからの相対URLでのアクセス
withDefaultUrl("http://b.hatena.ne.jp/"); goTo("/it");
Fluent#goTo(java.lang.String))
Fluent#withDefaultUrl(java.lang.String))
要素の特定方法
色々とやり方がありますが、ここではFluentTest#$とFluentTest#findを紹介します。 両方ともjQuery記法がそのまま使えます。
FluentTest#$
$("input[name=password]").text("foo");
FluentTest#find
$のセレクタで特定した要素の子階層から絞り込むために利用します。 jQueryのようにメソッドチェーンでどんどん指定していくことができます。要素を特定したら、アクションを発火します。
$(".sample-class").find("span").click();
Wait処理
FluentTest#awaitを利用し、取得したFluentWaitのインスタンスにて細かい条件を付けていきます。
タイムアウトを設定した上で、要素を特定した条件を付けます。ここでも、要素の特定にはjQueryの記法が使えます。
await().atMost(5, TimeUnit.SECONDS).until(".small").hasSize(3);
また、FluentWait#untilが戻すFluentWaitMatcherのインスタンスにて様々な条件が設定可能です。
hasText("myTextValue")
isPresent()
isNotPresent()
hasId("myId")
hasName("myName")
containsText("myName")
areDisplayed()
areEnabled()
JavaDoc FluentTest#await FluentWait FluentWaitMatcher
PageObjectsパターン
Seleniumでは画面ごとにクラスを作成して、テストコードではそのクラスのインスタンスを操作するだけというPageObjectsのデザインパターンがよく利用されますが、FluentLeniumのAPIはPageObjectsパターンを想定して作られています。
1. PageObjectのクラスを作成します。
org.fluentlenium.core.FluentPageクラスを継承して作成します。 FluentPage#getUrlをオーバーライドし、画面のURLを返却させます。
import org.fluentlenium.core.FluentPage; /** * ログイン画面を表すPage Objectクラス。 */ public class LogicPage extends FluentPage { @Override public String getUrl(){ return "http://xxx.xxxx/login"; } /** * 指定された認証情報でログインを行います。 * @param userCd * @param password */ public void login(String userCd, String password){ await().until("#user_cd").areDisplayed() .find("#user_cd").text(userCd); $("#password").text(password); $(".btn-login").click(); } }
2. テストコードにてPageObjectのクラスを呼び出し、操作します。
@Pageアノテーションを指定したインスタンス変数を宣言することで、FluentTest側で適切にPageObjectのインスタンスを生成してくれます。 PageObjectが提供する画面にアクセスするには、FluentTest#goTo(P page)を利用します。
import org.fluentlenium.adapter.FluentTest; import org.fluentlenium.core.annotation.Page; import org.junit.Test; import org.openqa.selenium.WebDriver; import org.openqa.selenium.chrome.ChromeDriver; public class SampleFluentLenium extends FluentTest { @Page public LogicPage loginPage; @Test public void testExecute() { goTo(loginPage); loginPage.login("user_cd", "password"); } }
3. 注意事項
テストコード側のクラスにてwithDefaultUrlを使ってベースURLをセットしても、PageObjectのgetUrlのURLにアクセスする際には適用されません。