FluentLeniumでJavaのSeleniumスクリプトをシンプルに - 前編

2015/02/09
FluentLenium

Selenium WebDriverが利用されるにつれて、Selenium WebDriverのコマンドをラップして、よりシンプルに書けるようにしたライブラリもいくつか登場してきました。RubyのCapybara、GroovyのGebなどは聞いたことのある方も多いでしょう。

Javaで同じようなことを実現したい場合には、FluentLeniumというライブラリがよく知られています。今日はこのFluentLeniumについてご紹介します。

目次

FluentLeniumとは

FluentLeniumは、JavaでSelenium WebDriverコマンドをシンプルに書けるライブラリです1  。フランスの女性開発者Mathilde Lemee氏が中心となって開発を進めています。Gebと似たAPIもあるので、影響は受けていると思われます。

Selenium WebDriverを薄くラップしたライブラリなので動作が比較的追いやすく、主要なコマンドはGitHubトップのREADME1ページにまとまっているので、Java + Selenium WebDriverと比べても学習コストはあまり高くないと思います。

有名なところでは、JavaやScalaのWeb開発フレームワークであるPlay FrameworkがFluentLeniumを採用しています2。また、日本でも既に何人かの方にブログなどで紹介されており、こちらの情報も参考になります。3

FluentLeniumの特徴の中でも、まず取り上げたいのは次の3つです。

まずはこの3つを見ていきましょう。

特徴1: シンプルなテストスクリプト

図1は、FluentLeniumを使ったブラウザ操作スクリプトのサンプルです。

public class SampleTest extends FluentTest {

    @Test
    public void test() {
        goTo("http://***");
        fill("#search").with("テスト");
        click("#go_button");
        assertThat(title(), is("検索結果"));
    }
}

FluentTestクラスを継承すれば、ブラウザの起動/終了の処理は自動的に行われます。各ブラウザ操作のメソッドも、非常にシンプルなものです。

goTo(<URL>)

指定されたURLにページ遷移します。

click(<対象要素>)

HTML要素をクリックします。対象要素は「CSSセレクタ」で指定しますが、これについては後ほど説明します。

fill(<対象要素>).with(<テキスト>)

HTML要素にテキストを入力します。

fillSelect(<対象>).withValue(<value属性の値>)

select要素(プルダウン)の指定された値の項目を選択します。

takeScreenShot(<ファイルパス>)

ブラウザの画面キャプチャを取得します。

executeScript(<JavaScriptコード>)

指定されたJavaScriptコードを実行します。

await().atMost(<ミリ秒>).until(<対象要素>).areDisplayed()

要素が表示されるまで、最大で指定されたミリ秒の間待機します。待ち処理には、この他にも様々な条件で待機するメソッドがあります。

Selenium WebDriverのメソッドをそのまま使うのと比べ、随分シンプルに書けるようになっています。

特徴2: jQueryライクなセレクタ

FluentLeniumの操作対象HTML要素の指定は、jQueryに似たスタイルで記述します(jQuery相当の機能で実装されているのは現状ごく一部ですが、テストで使うにはだいたい事足りるのではないか思います)。

指定に使うのは、CSSセレクタと呼ばれる記法です4 。これは普段JavaScriptを書いている方にはおなじみの記法でしょう。

// idが「user」の要素をクリック
click("#user");
// classが「small」の要素をクリック
click(".small");
// タグ名が「button」の要素をクリック
click("button");
// タグ名が「button」、classが「small」の要素をクリック
click("button.small");
// idが「user」の要素の子孫要素から、タグ名が「span」のものを取得しクリック
click("#user span");

findメソッドを使った書き方もできます。

// idが「user」の要素をクリック
find("#user").click();

さらに、findの代わりに「$」というメソッドを使うことで、jQueryライクな書き方になります。

// idが「user」の要素をクリック
$("#user").click();

with系のメソッドで追加の絞り込み条件を指定できます5 。name属性やテキストで要素を特定する場合は、これらのメソッドを使うのが良いでしょう。

// タグ名が「input」、name属性が「address」の要素をクリック
click("input", withName("address"));
// タグ名が「a」、テキストが「次へ」の要素を取得しクリック
click("a", withText("次へ"));

部分一致なども可能です。

// タグ名が「a」、テキストに「確認」を含む要素を取得しクリック
click("a", withText().contains("確認"));

findメソッドで探索した要素の子孫要素に対し、さらに探索を行うこともできます。

// idが「search」の要素の子孫要素からタグ名が「div」のものを取得し、
// 各要素のテキストを取得
find("#search").find("div").getTexts()

特徴3: ページオブジェクトパターンのサポート

「ページオブジェクトパターン」とは、UI変更時のスクリプト修正の手間を軽減するために、「Webページへの操作を、ページごとのクラスを作って1カ所にまとめる」技法です6 。FluentLeniumには、ページクラスの作成をサポートする継承元クラス「FluentPage」が用意されています。

図2は、「問い合わせページ」への操作をまとめた「ContactPage」クラスの例です。

// FluentPageクラスを継承する。
public class ContactPage extends FluentPage {

    // ユーザー名を入力する
    public void setName(String name) {
        fill("input", withName("your-name")).with(name);
    }

    // メールアドレスを入力する
    public void setMail(String mail) {
        fill("input", withName("your-email")).with(mail);
    }

    // ... 途中略 ...

    // 入力情報を送信する
    public void send() {
        click("input.wpcf7-submit");
    }

    // このページのURLを返す。
    // 共通設定で指定したベースURLからの相対パスも可。 
    @Override
    public String getUrl() {
        return "http://www-demo.trident-qa.com/contact/";
    }

    // ページ遷移が成功したかをチェックする。
    @Override
    public void isAt() {
        if (!"お問い合わせ".equals(title())) {
            throw new IllegalStateException("不正なページ:" + title());
        }
    }
}

setName、setMail、sendなどのメソッドが問い合わせページへの操作にあたるメソッドです。さらに、継承元に定義されているgetUrlとisAtメソッドをオーバーライドして実装します。7

テストケース中では、図3のようにContactPageクラスを介してブラウザ操作を行います。

@Test
public void 問い合わせが成功すること() {
    ContactPage contact = new ContactPage();
    goTo(contact); // getUrlメソッドで指定されたURLに移動
    contact.isAt(); // 指定ページに移動できたかチェック
    contact.setName("テストユーザー");
    contact.setMail("***@***.com");
    contact.setOrganization("TRIDENT Inc.");
    contact.setSubject("テスト");
    contact.setMessage("テスト送信です");
    contact.send();
}

「問い合わせページ」の情報を1クラスにまとめることで、UI変更がおきた場合でも1クラスだけを変更すればよくなります。

注釈・出典
  1. JavaのSeleniumコマンドのラッパーとしては、他にSelenideというライブラリもありますが、日本ではFluentLeniumの名前をよく聞きます。GitHubのContributor数やStar数からもFluentLeniumの方がメジャーな印象を受けました。 []
  2. Writing functional tests []
  3. FluentLeniumの紹介について」「FluentLenium」「テスト自動化の様々な道具を使ってみた四方山話」「なぜ画面自動テストはうまく行かないのか」など、参考にさせていただきました。 []
  4. 意外と知らない!?CSSセレクタ20個のおさらい」に、CSSセレクタの記法について分かりやすくまとめられています。 []
  5. 「import static org.fluentlenium.core.filter.FilterConstructor.*」を、with系のメソッドを使う時はあらかじめ行っておきます。 []
  6. 「ページオブジェクトパターン」について詳しく知りたい場合は、「Page Objects」や「4時間で学ぶ、効率的な自動テストスクリプトのメンテナンス」が参考になります。 []
  7. FluentLeniumのREADMEページではisAtメソッドをassertThatメソッドを使って実装していますが、Seleniumのページオブジェクトパターンの原則の1つに「Assertionをページクラスに含めるべきではない」という原則があるので、ここではassertThatは使わず例外をthrowしています。Selenium公式wikiページのサンプルコードもそうなっています。 []

コメントを残す

メールアドレスが公開されることはありません。 * が付いている欄は必須項目です