Tellurium を試してみた
もう25日は過ぎてしまいましたが、自重せずに G* Advent Calendar 2011 : ATND 27日目の記事行かせていただきたいと思います。
初めまして、id:yukung/@yukung と申します。Groovistな皆様、お手柔らかにお願いいたします。
最近、ノンブロッキングI/Oな仕事をしているので、当初Grettyについて書こうかなーと思っておりました。そうしたら見事に @toby55kij さんとネタがかぶってしまったので(Grettyで割り勘計算機 #gadvent2011 - Toby55@新潟の日常)、慌ててここを漁って違うネタを探して若干焦りました><
Groovy - Modules の中で、Telluriumがちょっと面白そうだなーと思ったのと、ググッてもあまり日本語の情報がなかったので*1、書いてみようと思います。というわけで、急造な記事なのでちょっと浅いかもしれませんが、ご容赦ください。
Tellurium って?
Tellurium は、GroovyとJavaで実装されたWebアプリケーション向けのテスティングフレームワークです。この分野には、Webブラウザの操作をエミュレートしてWebアプリケーションの自動テストを可能にしてくれるSeleniumという有名なツールがありますが、TelluriumはそのSeleniumを基盤にして構築されています。というか私が触ってみた感想は、平たく言ってSeleniumをDSLで書けるようにしたGroovy/Javaラッパーな感じです。
Selenium との違い
公式サイトによれば、TelluriumはSeleniumとの違いを、テストコンセプトの違いと定義しています。また、Seleniumを「C言語」のようなものであるとすれば、Telluriumは「C++」のようなものだ、とも言っています。なんだかわかったようなわからないような感じですね。
UI要素の扱い
Telluriumでは、UI要素の記述を「UI module」として扱えることをSeleniumに対するアドバンテージとして捉えているようです。
RIAやAjaxなど*2、リッチなWebアプリケーションが一般的になってきた昨今、Webのインテグレーションテストも重量級になってきがちなところに対する一つの答えとなるのがTelluriumのゴールのようです。
主なメリットとしては、
- JavaScript Eventをトリガーにした動作
- Ajaxアプリケーションのサポート
- 変更に対する堅牢性と柔軟性
- メンテナンスの容易さ
- UI module の再利用性
- 表現力(DSLで書けるなど)
といった所が挙げられています。
私はSeleniumを仕事では使っていませんし、使いたいなーと思って入るものの今のところヘビーユーザーでもないので、相違点について細かいところはあまりわからないのですが、例えばSeleniumでボタンクリックするような挙動を記述したいときは、
selenium.click("//div[3]/input[@value='Create']");
こんな風に書くと思いますが、TelluriumではGroovyを使って、
ui.Form(uid: "Form", clocator: [tag: "form"]){
Div(uid: "User", clocator: [:]){
Selector(uid: "Sex", clocator: [:])
InputBox(uid: "Input", clocator: [tag: "input", type: "text", name: "j_username"])
}
Container(uid: "Finish", clocator: [tag: "tr"]){
SubmitButton(uid: "Submit", clocator: [tag: "input", type: "submit", value: "Login", name: "submit"])
}
}
こんな風に、UI要素だけを分離して書くことで、テスト箇所のUI要素と、実際のテストコードを分離することができます。また、UI要素を階層構造で記述するので、モジュールとしての再利用性も期待できます。
UI要素など、変更が頻繁にかかる箇所はGroovyのDSLでライトに書いてカプセル化し、テストコードは使い慣れたJUnitやTestNGなどで書くことによって、UIの変更に大きな影響を受けず、かつUI要素をモジュール化して再利用できる、ということが主なメリットのようです。
チュートリアル
というわけで、百聞は一見にしかずということで実際に動かして試してみました。
お題
公式サイトのチュートリアルをそのまま試して見ました。Google Codeのwikiページの検索操作をブラウザから自動テストするサンプルです。
準備
以下のツール・ライブラリを使って動作させてみました。当初Macでやってたのですが、TelluriumのFirefoxプラグインがFirefox 3 までしか対応しておらず、MacのFirefox 3 で動かそうとするとlibsql3.dylibのバージョン周りでハマったので、Windowsで動かしました。TelluriumのFirefoxプラグインが最近のFirefoxに追随しない限り、Macは鬼門だと思います。最近更新が停滞気味でこの先微妙かも…
- Windows 7
- JDK 1.6
- Maven 2.2
- Intellij IDEA 11
- Firefox 3.6
Mavenからプロジェクト作成
Telluriumのプロジェクト作成方法はいくつかありますが、ここではMavenを使ってプロジェクトを作ります。
まず初めに、Telluriumのリポジトリを~/.m2/setting.xmlに記述しておきます。
- ~/.m2/setting.xml
<settings>
<profiles>
<profile>
<activation>
<activeByDefault>true</activeByDefault>
</activation>
<repositories>
<repository>
<id>kungfuters-public-snapshots-repo</id>
<name>Kungfuters.org Public Snapshot Repository</name>
<releases>
<enabled>false</enabled>
</releases>
<snapshots>
<enabled>true</enabled>
</snapshots>
<url>http://maven.kungfuters.org/content/repositories/snapshots</url>
</repository>
<repository>
<id>kungfuters-public-releases-repo</id>
<name>Kungfuters.org Public Releases Repository</name>
<releases>
<enabled>true</enabled>
</releases>
<snapshots>
<enabled>false</enabled>
</snapshots>
<url>http://maven.kungfuters.org/content/repositories/releases</url>
</repository>
</repositories>
</profile>
</profiles>
</settings>
それから、mvnコマンドを叩いてプロジェクトを作ります。テストコードにJUnitを使ったarchetypeと、TestNGを使ったarchetypeの2通りがあるので、お好きな方を。今回はJUnitのarchetypeを使います。
mvn archetype:create -DgroupId=org.yukung -DartifactId=tellurium-sample \
-DarchetypeArtifactId=tellurium-junit-archetype \
-DarchetypeGroupId=org.telluriumsource \
-DarchetypeVersion=0.7.0 \
-DarchetypeRepository=http://maven.kungfuters.org/content/repositories/releases||<
TestNGの場合は、こんな感じ。
mvn archetype:create -DgroupId=org.yukung -DartifactId=demo \
-DarchetypeArtifactId=tellurium-testng-archetype \
-DarchetypeGroupId=org.telluriumsource \
-DarchetypeVersion=0.7.0 \
-DarchetypeRepository=http://maven.kungfuters.org/content/repositories/releases
正しくmavenがビルドしてくれれば、以下のようなディレクトリ構成でプロジェクトディレクトリが作られます。
$ find .
.
./demo
./demo/pom.xml
./demo/README
./demo/src
./demo/src/main
./demo/src/main/groovy
./demo/src/main/resources
./demo/src/test
./demo/src/test/groovy
./demo/src/test/groovy/module
./demo/src/test/groovy/module/GoogleSearchModule.groovy
./demo/src/test/groovy/module/JettyLogonModule.groovy
./demo/src/test/groovy/test
./demo/src/test/groovy/test/GoogleSearchJUnitTestCase.java
./demo/src/test/groovy/test/JettyLogonJUnitTestCase.java
./demo/src/test/resources
./demo/TelluriumConfig.groovy
InteliJ IDEA にプロジェクトインポート
次に、作ったMavenプロジェクトをIDEにインポートします。多分Mavenでテスト走らせることもできますが、最初はIDE使ったほうがわかりやすいかなーと。
使うIDEは、一応
- IntelliJ IDEA
- Eclipse(Groovy-Eclipse Plugin)
- NetBeans
と一通り使えます。GroovyとJava両方使うのでIntellJ IDEAが一番楽です。あと、テストコード実行がすんなり行ったのがIntelliJ IDEAだったので、これを使っています。これを気にIntellJ IDEAも使いこなせるようになりたいな。
- 上部メニューから File -> New Project…

- Import project from external modelを選択

- Mavenを選択

- Root directory にMavenで作ったディレクトリを指定

- version指定はそのまま次へ

- プロジェクト名を指定

- インポートできるとこんな感じ

TrUMPインストールとUI Moduleの作成、カスタマイズ
プロジェクトができたら、いよいよテストコード作成に入ります。Telluriumでは、先述の通りテストコードとUI要素を分離して記述します。先にUI要素としての UI module を作成します。
UI module は、「TrUMP」と呼ばれるプラグインを使って作成します。*3「TrUMP」は、ブラウザでテスト対象の要素を操作することでUI module とテストコードの雛形を自動生成してくれるツールです。
注意点として、「TrUMP」は2011年12月現在、Firefox 3 までしか対応していません。Firefoxの最新版がインストールされている場合は別途旧バージョンとして3.6をインストールしておかないとダメです。複数バージョンの共存の方法はGoogle先生に聞いてください。
TrUMPがインストールされていれば、Firefox のメニューバー -> ツール -> TrUMP IDE から、プラグインが呼び出せます。
※ここからキャプチャがWindowsになってますがスルーの方向でw

テストしたい画面を開いて操作をキャプチャしていきます。TrUMP IDEの「Record」というボタンが押されていれば、キャプチャ状態です。止めたい場合は「Stop」です。
ここでは、Searchというセレクトボックスから「All downloads」を選択して、forというテキストボックスにフォーカスをあて、Searchボタンをクリックする、という3つの操作を行いました。(操作した箇所が緑色に変わります。)

すると、UI Moduleタブを開いて見てみると、UI moduleのひな形コードが生成されています。

さらに、UI要素一つ一つにユニークな名前を付けていきます。これはテストコードから要素を指定するときに使います。わかり易い名前にしておくといいと思います。FormのUIDを"TelluriumDownload"、Groupにチェックを入れて*4「Save」ボタンをクリック。

SelectorのUIDを"DownloadType"にして「Save」ボタンをクリック。

InputBoxのUIDを"Input"にして「Save」ボタンをクリック。

SubmitButtonのUIDを"Search"にして「Save」ボタンをクリック。

出来上がったUI moduleコード。

このあと、メニューバーのFile -> Export で任意の場所に.groovyファイルとして保存します。エクスポートされたファイルは、UI module のコードとテストコードのひな形が一着のファイルで出力されます。
//----------------------- NewUIModule.groovy ------------------------
package test
import org.telluriumsource.dsl.DslContext
/**
* This UI module file (NewUIModule.groovy) is automatically generated by TrUMP 0.8.0.
* For any problems, please report to Tellurium User Group at:
* http://groups.google.com/group/tellurium-users
*
* Example: Google Search Module
*
* ui.Container(uid: "Google", clocator: [tag: "td"]){
* InputBox(uid: "Input", clocator: [title: "Google Search"]
* SubmitButton(uid: "Search", clocator: [name: "btnG", value: "Google Search"]
* SubmitButton(uid: "ImFeelingLucky", clocator: [value: "I'm Feeling Lucky"]
* }
*
* public void doGoogleSearch(String input) {
* keyType "Google.Input", input
* click "Google.Search"
* waitForPageToLoad 30000
* }
*
*/
class NewUIModule extends DslContext {
public void defineUi() {
ui.Form(uid: "TelluriumDownload", clocator: [tag: "form", method: "GET", action: "list"], group: "true"){
Selector(uid: "DownloadType", clocator: [tag: "select", direct: "true", name: "can", id: "can"])
InputBox(uid: "Input", clocator: [tag: "input", type: "text", name: "q", id: "searchq"])
SubmitButton(uid: "Search", clocator: [tag: "input", direct: "true", type: "submit", value: "Search"])
}
}
//Add your methods here
}
//----------------------- MyTestCase.java ------------------------
package test;
import org.telluriumsource.test.java.TelluriumJUnitTestCase;
import org.junit.*;
/**
* This test file (MyTestCase.java) is automatically generated by TrUMP 0.8.0.
*
*/
public class MyTestCase extends TelluriumJUnitTestCase {
private static NewUIModule num;
@BeforeClass
public static void initUi() {
num = new NewUIModule()
num.defineUi();
connectSeleniumServer();
useTelluriumEngine(true);
}
//Add your test cases here
@Test
public void testCase(){
...
}
}
このNewUIModule.groovyを、${PROJECT_HOME}/src/test/groovy/module/へ、NewTestCase.javaを${PROJECT_HOME}/src/test/groovy/testへ取り込みます。

NewUIModule#defineUI()メソッドが、UI要素を定義するメソッドになります。
//Add your methods here のコメント以下には、テストコードから呼び出したいメソッドを定義しておきます。ここでは、
- NewUIModule#searchDownload(String keyword)
- NewUIModule#getAllDownloadTypes()
- NewUIModule#selectDownloadType()
の3つを定義しておきます。
テストコード作成
UI module はできたので、テストコードの方を書きます。テストコードはプロジェクト作成時にJUnitのarchetypeを選択したので、JUnit4ベースで書きます。
ここまでで、テスト実行の準備は整いました。

実行
実行時の設定として、プロジェクトルートのTelluriumConfig.groovyというファイルがあり、これがTellurium(Seleniumの設定をGroovyで記述したもの)の挙動を司っています。
コメント付きなのである程度内容はわかると思いますが、SeleniumServerを起動するときに使う設定は全てここで記述します。
tellurium.embeddedserver.profileにfirefoxのprofileを記述すると、そのprofileでFirefoxが起動します。なので、ここにFirefox3用のprofileを別途作ったものを指定することで、複数バージョンのFirefoxを共存して使えるようにしています。
また、tellurium.connector.browserにSeleniumが起動して使用するブラウザを指定できます。Firefox,IE,Chrome,Safari,Operaなど、主要なブラウザは抑えているので、マルチブラウザ環境での確認も行えるようになっています。
その他設定内容については、公式サイトのWikiやSeleniumの設定を参考にするといいと思います。
あとは、IntelliJ IDEAのプロジェクトツリーからテスト対象のファイルを右クリックして、Run “NewTestCase” でテストを実行すると、Selenium Serverが起動して、

Selenium RC 経由でFirefoxが起動、

テストコードで記述した内容を実行してテスト結果を表示してくれます。

感想
ということで、Telluriumのチュートリアルを通してプロジェクト作成 -> UI module 作成 -> テストコード作成 -> テスト実行 という一通りの流れを体験して見ました。
やってみた感想としては、GroovyでUI要素を構造化して記述できると、テストコードがSeleniumのコードよりもわかりやするというのと、テストコードはJUnitなりTestNGなりで慣れた書き方ができる*5ので、テストコードのメンテナンスがしやすくなるだろうなぁという印象を受けました。
また、良い意味でも悪い意味でもSeleniumをラッピングしてくれているので、単にテストコード書く分にはそれほどSeleniumの知識は必要なさそうだとも感じました。*6
逆に大変だと思ったところとしては、
-
環境構築が大変
-
MacだとFirefox,Google Chrome,Safariで試してみたところ、Seleniumからブラウザ起動するところでうまく動作せず全滅
- Firefoxの中で使っているlibsqlite3.dylibがバージョン互換性がない?⇛使えない?
-
Windows なら比較的うまく動いてる
-
-
UI Module作成用のプラグイン「TrUMP」が今のところFirefox 3限定なのでこれだけのためにブラウザ別に入れるのに抵抗がある
-
ハマったときにやっぱり情報が少ない
- 一応公式サイトのWikiと、PDFの英語ドキュメント(200ページオーバーの大作!)があるけど…
-
プロジェクトそのものが現在活発じゃなさそうで、Tellurium本体、プラグイン共に更新が滞りがち
- スマフォ対応とか、WebDriverのAPI対応とかは期待できなさそう
-
UI module をGroovyで気軽に書けるのはいいけど、その分他の所で足を引っ張られるところが多そうでメリデメ天秤にかけたときに、Selenium単体でGroovy使ってテストコード書いてもいい気がしてきた
といった感想を持ちました。
まとめ
まとめとしては、万人にオススメできてGroovyでウハウハ!な結果になったとは言えず、誰得なエントリになってしまいましたが、ご興味あれば触ってみるといいのではないでしょうか。
※今回作成したプロジェクトは、Githubへアップしています。
*1:というか英語の情報も多くなかった
*2:あまり聞かなくなりましたねこの言葉
*3:ベタで書いてもいいとは思いますが
*4:要素が構造化されます
*5:もちろんGroovyでassert書いてもいいはず
*6:ただ、ハマったらやっぱりSeleniumの知識は必要だと思う