Gebを使ってみた。

G*系の勉強会で何回か聞いたことあるので名前は知っていましたが、使ったことはなかったのでとりあえず使ってみました。
資料が少なすぎる、あっても古くてそのままじゃ使えない、色々わかってる前提で書かれてる等々、苦労はありましたがなんとか動作する所までできました。

Gebとは

Webアプリケーション向けの機能テストを自動化するためのライブラリです。
Internet Explorer, FireFox, Chrome などのブラウザを操作することが可能なSeleniumのWebDriverとJQueryライクな記述でコンテンツの操作や検査を可能にするAPIを組み合わせ、
Groovyの豊かな表現力により簡潔なDSLでテストスクリプトを記述することができます。

http://beta.mybetabook.com/showpage/4f27c8cc0cf26106dca875c8

私は「ブラウザをプログラムで操作できます。」「Seleniumより簡単に書けます。」と、理解しました。
後、「Groovyで書けます。」って事。

事前準備

IDE

情弱なのでEclipseを使用。

初期設定

  • Gradleプロジェクト作成
  • build.gradle記述
    • ブラウザのバージョンはどんどん上がっていくので、seleniumのバージョンが低いと動かない可能性が高いです。最新版推奨。*1
apply plugin: 'groovy'
apply plugin: 'eclipse'

repositories {
    mavenCentral()
}

dependencies {
    compile 'org.seleniumhq.selenium:selenium-firefox-driver:2.31.0'
    compile 'org.codehaus.geb:geb-core:0.7.2'
}
  • プロジェクト右クリック → Gradle → 全てリフレッシュ
  • 「src/main/groovy」をソースフォルダとして追加
  • 「target」をフォルダとして追加
  • プロジェクト作成時にサンプルとして入っているソースコードを除去

HTML作成

ブラウザ上で動かすHTMLを作成。(src/main/resources)

index.html
<!DOCTYPE html>
<html lang="ja">
<head>
<meta charset="UTF-8">
<title>index</title>
<script type="text/javascript">
	function testfunc() {
        document.location = "./greeting.html";
	}
</script>
</head>
<body>
	<form action="">
		<input type="text" name="username"/>
		<input type="button" value="CLICK ME" onclick="testfunc()" name="greet"/>
	</form>
</body>
</html>
greeting.html
<!DOCTYPE html>
<html lang="ja">
<head>
<meta charset="UTF-8">
<title>Greeting</title>
</head>
<body>
    <h1>Greeting Page.</h1>
</body>
</html>

ページクラス作成

各ページに対応したクラスを作成

IndexPage.groovy
package com.yamap55.sample.geb;

import geb.Page

public class IndexPage extends Page {
    // ブラウザに渡されるので相対パスはNG
    static url = "file:///c:/work/yamashita/workspace/geb_sample/src/main/resources/index.html"
    static at = { title == 'index' }
    static content = {
        // わかりやすくするためにHogeを後ろにつけています。
        usernameHoge {
            $('form').username = it
        }
        greetButtonHoge(to:GreetingPage) {
            $('input', name:'greet')
        }
    }
}

サンプルではusername、greetButtonとなっていましたが、どういう経由で実際に値を詰めてるのかわからなかったので、
ここではusernameHogeとgreetButtonHogeとしています。

GreetingPage.groovy
package com.yamap55.sample.geb;

import geb.Page

public class GreetingPage extends Page {
    static at = { title == 'Greeting' }
    static content = {
        h1Text { $('h1').text() }
    }
}

Geb実行クラス作成

実際にブラウザでHTMLを表示し、操作させるクラスの作成。
ここは処理毎に説明を入れます。

クラス全体

GebSample.groovy

package com.yamap55.sample.geb

import geb.Browser
import geb.Configuration

println "START"
Configuration conf = new Configuration()
conf.baseUrl = "."
conf.driverConf = "firefox"
conf.reportsDir = new File("./target")

Browser.drive(conf){
    to IndexPage
    assert at(IndexPage)
    report "index_page"

    usernameHoge "ユーザ名"
    greetButtonHoge.click()

    assert at(GreetingPage)
    report "greeting_page"

    assert h1Text == "Greeting Page."

    quit()
}
println "END"
各種設定
Configuration conf = new Configuration()
conf.baseUrl = "."
conf.driverConf = "firefox"
conf.reportsDir = new File("./target")

driverConfはブラウザの種別、reportsDirはレポートを出力するディレクトリです。
baseUrlは何に使ってるのかわかりませんが設定しないとエラーになりました。

ブラウザ起動
Browser.drive(conf){
    to IndexPage

ここで上で設定した通りfirefoxが起動され、指定されたページを表示します。
pageクラスに指定したurlが誤っているとここで落ちます。

ページの表示確認?
assert at(IndexPage)

ページクラスで指定したatクロージャーが実行されます。
今回はIndexPage.groovyで作成した通りタイトルがindexとなっているかを確認しています。(サンプルのまま。)
実際にはページの要素が正しいかなどの表示確認などを行う?

static at = { title == 'index' }
レポート出力
report "index_page"

各種設定で設定したレポート出力ディレクトリにレポートが出力されます。
実際にブラウザで表示されたHTMLとキャプチャが出力されるようです。(index_page.htmlとindex_page.png
この一行でHTMLとキャプチャが取得されるのは嬉しい!

conf.reportsDir = new File("./target")
テキストボックスに値設定
usernameHoge "ユーザ名"

ページクラスで定義したクロージャを呼び出して値を設定します。(「usernameHoge("ユーザ名")」と同じです。)
本当は参考にあるように「usernameHoge = "ユーザ名"」としたかったのですが、やり方がわからず。
変数に入れるだけだとクロージャが呼ばれないのでうまくいかないような気がするのですが、何か方法あるのでしょう。

usernameHoge {
    $('form').username = it
}
ボタンクリック
greetButtonHoge.click()

テキストボックスに値を設定する時と同じようにページクラスに定義したクロージャを呼び出して、
返り値のボタンのオブジェクトのクリックメソッドを呼び出します。*2
要素の指定がjqueryライクでわかりやすくて素敵です。

greetButtonHoge(to:GreetingPage) {
    $('input', name:'greet')
}
別のページへ遷移

index.htmlのボタンをクリックした事でgreeting.htmlに遷移しています。

assert at(GreetingPage)
report "greeting_page"

なので、GreetingPageクラスの表示確認を行なってレポートを出力。

static at = { title == 'Greeting' }
h1要素を確認
assert h1Text == "Greeting Page."

greeting.htmlに表示されているh1要素を確認します。
これもGreetingPageクラスに定義されているh1Textクロージャの返り値を比較します。

static content = {
    h1Text { $('h1').text() }
}
ブラウザを閉じる

ここはサンプルになかったのですが、閉じないと処理が終了しないようなので加えました。

quit()

感想とか

メリット
  • Seleniumを使用したのが1年前位?なのでウル覚えですが、Seleniumより簡単にな気がする。
    • 特にPageクラスが既にあるので、ページオブジェクトの作成が楽になったのはよい。
  • レポート機能でキャプチャだけじゃなくてソースも取得してくれるのは動的な画面作成時に助かると思う。
  • ぱっと見での可読性が高いのはいいですよね。文章で読めるのは処理がわかりやすい。
デメリット
  • 参考文献が少ない(特に日本語だと。。。)
    • マニュアル等でコードはあるのでなんとかなるか。
  • どの処理がどこで動作するのかがわかりにくい。
    • Groovyに慣れればなんとかわかるか?(IDEAとか使うともっといい感じにわかるのかも)

*1:ただ、最新版は不都合がある事も。。。

*2:geb.navigator.NonEmptyNavigatorというクラスでした。