jjug ccc 2011 fall / web test automation with geb and spock
DESCRIPTION
TRANSCRIPT
Geb と Spock でWeb テストを自動化せよ
2011/10/17 @JJUG CCC 2011 Fall JJUG 幹事 / JGGUG サポートスタッフ 須江 信洋
http://twitter.com/nobusue http://d.hatena.ne.jp/nobusue
※ 資料の内容は個人としての意見・見解を述べたものであり、所属する企業・組織が内容を保証するものではありません。
自己紹介 須江 信洋(すえ のぶひろ)
Twitter: @nobusue http://www.facebook.com/profile.php?
id=732337788 かれこれ 10 年位、 JavaEE 関連の仕事をしてま
す G*(Groovy 関連技術 ) との関わり
Groovy コミュニティ (JGGUG) サポートスタッフ 「プログラミング GROOVY 」執筆チーム 「 Groovy イン・アクション」翻訳チーム Groovy で作った Bot 飼ってます (@hatena_groovy)
2
本日のお題 なぜテスト自動化が大切なのか Web テスト自動化ツール Selenium の系
譜 Geb とは Spock とは Geb と Spock のインテグレーション
3
テスト自動化できてますか? V モデル
4
http://ja.wikipedia.org/wiki/V%E3%83%A2%E3%83%87%E3%83%AB
ここは JUnit などでわりかし自動化できている
ここは未だに人海戦術が主流
自動化できれば・・・ テスト実行に伴う人的コストが不要にな
る リファクタリングに取り組み易くなる バグフィックスや機能修正によるリリース頻
度を上げられる ミドルウェアや OS の Fix 適用に躊躇しなくて
よくなる テストの品質を上げられる
手作業に完全ということはありえない 手作業は監査できない
5
Continuous Delivery UAT まで自動化することで道が拓ける
6
http://www.amazon.co.jp/dp/0321601912
なぜ自動化が進まないのか? テスト自動化にコストがかかりすぎる
アプリ側の問題 : テスト自動化を想定していない
ツールの問題 アプリの変更に追随するのが大変 そもそも自動化できない場合がある
ツールの制限など
7
Selenium の系譜
8
Selenium1(Selenium RC)
2004~
Selenium IDE
Selenium-Grid
WebDriver(Google)
2006~
Selenium2
Selenium2+WebDriver
2009~
2011/07Selenium2.0 リリース
http://seleniumhq.org/docs/01_introducing_selenium.html#brief-history-of-the-selenium-project
Selenium1 と WebDriver Selenium1 の課題
テスト・ドライバーがブラウザ上で稼働するため、ブラウザのサンドボックスの制限を受ける
原理的に対応が難しい機能がある ファイルアップロードなど Ajax 対応
WebDriver テスト・ドライバーがブラウザ外部で稼働するため上
記の制限を受けない Headless Driver(HtmlUnit) に対応 詳細な比較については以下 http://
www.asukaze.net/etc/webdriver/9
WebDriver: Java API サンプル
10
public class Selenium2Example { public static void main(String[] args) { WebDriver driver = new FirefoxDriver(); driver.get("http://www.google.com"); WebElement element = driver.findElement(By.name("q")); element.sendKeys("Cheese!"); element.submit(); System.out.println("Page title is: " + driver.getTitle()); (new WebDriverWait(driver, 10)) .until(new ExpectedCondition<Boolean>() { public Boolean apply(WebDriver d) { return d.getTitle().toLowerCase().startsWith("cheese!"); } }); System.out.println("Page title is: " + driver.getTitle()); driver.quit(); }}
Geb( じぇぶ ) とは
Groovy で構築された WebDriver のラッパー jQuery ライクな Navigator API を提供 Page Object パターンによる構造化 2011/10 時点での最新バージョンは 0.6
多様なテストフレームワークと統合可能 Spock,EasyB JUnit3/4,TestNG Cucumber(Cuke4Duke)
11
http://www.gebish.org/
jQuery-like Navigator API
12
// CSS 3 selectors$("div.some-class p:first[title='something']") // Find via index and/or attribute matching$("h1", 2, class: "heading")$("p", name: "description")$("ul.things li", 2) // 'text' is special attribute for the element text content$("h1", text: "All about Geb") // Use builtin matchers and regular expressions$("p", text: contains("Geb"))$("input", value: ~/\d{3,}-\d{3,}-\d{3,}/) // Chaining$("div").find(".b")$("div").filter(".c").parents()$("p.c").siblings()
Page Object パターン
13
class LoginPage extends Page { static url = "http://myapp.com/login" static at = { heading.text() == "Please Login" } static content = { heading { $("h1") } loginForm { $("form.login") } loginButton(to: AdminPage) { loginForm.login() } }}
class AdminPage extends Page { static at = { heading.text() == "Admin Section" } static content = { heading { $("h1") } }}
Browser.drive { to LoginPage assert at(LoginPage) loginForm.with { username = "admin" password = "password" } loginButton.click() assert at(AdminPage)}
ログイン画面
管理画面
テスト
Geb の例 : はてなキーワード検索
14
@Grapes([ @Grab("org.codehaus.geb:geb-core:latest.release"), @Grab("org.seleniumhq.selenium:selenium-firefox-driver:latest.release")])import geb.Browser
Browser.drive { go "http://d.hatena.ne.jp/keyword/" assert title == " はてなキーワード - 話題の言葉がわかる、みんなで編集するキーワード " $("form.header-search").word = "Groovy" $("form.header-search").find("input", name:"submit").click()
assert title == " はてな検索 : Groovy"}
Spock( すぽっく ) とは
Groovy で構築された BDD フレームワーク Groovy の動的型を活用した DSL を提供 Power Assert で問題箇所を容易に特定可能 可読性の高いテストケース データ・ドリブン・テストにより多数のテス
トパターンをコンパクトに記述可能 テストケースは JUnit から実行可能
15
http://code.google.com/p/spock/
Power Assert
16
def a = 1def b = 2def c = 3
assert (a+b)*c == 5
途中結果や、どこで fail したかまで教えてくれる。assertEquals() とかを組み合わせる必要ナシ。
可読性の高いテストケース
17
def "subscribers receive published events at least once"() { when: publisher.send(event) then: (1.._) * subscriber.receive(event) where: event << ["started", "paused", "stopped"]}
def "length of Spock's and his friends' names"() { expect: name.size() == length
where: name | length "Spock" | 5 "Kirk" | 4 "Scotty" | 6}
Geb と Spock のインテグレーション Geb と連携する Spock のテストケースと
して以下が提供される geb.spock.GebSpec / GebReportingSpec
browser インスタンスの注入 WebDriver の Browser クラスの初期化が不
要エビデンス取得の自動化
GebReportingSpec を利用すると、テストケースのメソッド終了時にスクリーンショット (PNG) が自動取得される
18
GebReportingSpec の例
19
@Grab("org.codehaus.geb:geb-spock:0.6.0")@Grab("org.spockframework:spock-core:0.5-groovy-1.8")@GrabExclude("org.codehaus.groovy:groovy-all")@Grab("org.seleniumhq.selenium:selenium-firefox-driver:latest.release")import geb.spock.GebReportingSpec
class FunctionalReportingSpec extends GebReportingSpec { def "Hatena Keyword Search Top"() { when: go "http://d.hatena.ne.jp/keyword/" then: title == " はてなキーワード - 話題の言葉がわかる、みんなで編集するキーワード " } def "Hatena Keyword Search Result"() { when: $("form.header-search").word = "Groovy" $("form.header-search").find("input", name:"submit").click() then: assert title == " はてな検索 : Groovy" }}
GebSpec利用時の注意点 Spock-0.5(2011/10 時点 ) では Groovy
の @Grab を利用する場合は、依存関係の兼ね合いで以下が必要 @GrabExclude("org.codehaus.groovy:groovy-
all")
GebReportingSpec を利用する場合はレポート出力先のディレクトリを指定しておく必要がある 最も簡単なのはシステムプロパティを使うこ
と groovy -Dgeb.build.reportsDir=/tempdir
GebSpockReporting.groovy20
JGGUG からのお知らせ G* ワークショップ
だいたい月 1回のペースで G* 関連の勉強会を実施しています
次回は 11/22予定、 Geb の詳しい話を予定 詳細は http://www.jggug.org/ で
G*Magazine http://grails.jp/g_mag_jp/ JGGUG が発行している電子雑誌です。創刊号と第 3号に、 @bikisuke さんが Geb/
Spock の技術情報を執筆されています。21
ご静聴ありがとうございました
22