1 
第2回 日本Seleniumユーザコミュニティ勉強会 
脱・独自改�造! 
GGeebbでWWeebbDDrriivveerrをもっとシンプルに 
株式会社SHIFT 
玉川紘子
2 
AAGGEENNDDAA 
n 自己紹介+α 
n Gebで便利になること 
n つまずくかもしれないこと 
n 工夫してみたこと 
n まとめ
3 
自己紹介 
名前:玉川 紘子(たまがわ ひろこ) 
所属:株式会社SHIFT ソフトウェアテスト事業本部 
   技術開発部 
コミュニティ:STAR(テスト自動化研究会) 
       日本Jenkinsユーザ会
4 
CCII・自動テストなんでも屋さんとして活動中 
開発言語:Java/PHP/Rubyなど 
業務でよく使うツール:Jenkins/Selenium 
メイン業務はCI・自動テストに関するなんでもお手伝い 
最近はJenkinsに関するセミナーなどもさせて頂いてます 
n 運用方針の提案 
n 実際に稼働するCI環境の構築 
n テストの書き方指南 
Jenkinsってどうやって 
使えばいいんだっけ? 
Seleniumで 
テストを書いてみたい 
んだけど…
5 
「実践 SSeelleenniiuumm WWeebbDDrriivveerr」を 
少しだけお手伝いしました 
n 翻訳+付録Bは玉川竜司さん 
n 私は付録Aとして、Seleniumと 
Jenkinsの連携について寄稿させて頂 
きました 
※竜司さんとは親戚ではありません 
今回は書籍の発売記念を兼ねてということで、 
竜司さんのおまけで声をかけていただきました。
6 
…が、
7 
Selenium×Jenkins連携の話は 
前回してしまったので(泣)、 
今⽇日は別のお話です。 
Geb
8 
Gebのお話に行く前に 
WWeebbDDrriivveerr、そのままでは微妙ですよね 
n ちょっとしたことを書くにも、長い。。。 
// テキスト入力 
WebElement element = driver.findElement(By.name(“hoge”)); 
element.clear(); 
element.sendKeys(); 
// プルダウンの選択 
Select select = new 
Select(driver.findElement(By.name(“fuga”))); 
select.selectByValue(“00”); 
n 大体みんな同じようなラッパーを作っている 
n と、前回の勉強会に出て思いました。
9 
Gebのお話に行く前に 
「あるある」で共感している場合なのか 
n みんなで同じものを作るのは勿体ない 
n OSSで提供されているライブラリも色々 
n FluentLenium(Java) 
n Geb(Groovy) 
n テスト対象製品に依存しないような改良は、独自に行うの 
ではなく既存ライブラリに頼ってしまおう
10 
GGeebbとは 
Gebで便利になること 
n 読み方は「じぇぶ」 
n http://www.gebish.org/ 
n Groovyで動くWebDriverのラッパー 
n jQueryライクでわかりやすいLocator 
n コード量が少なく、すっきり書ける 
n テスト自動化研究会で@kyon_mmさんから教えてもらいま 
した
11 
Gebで便利になること 
ツールの立ち位置としてはこんな感じ 
クロスブラウザ対応 データ駆動テスト 
ブラウザ操作 WebDriver Geb 
テスティング 
フレームワーク 
JUnit 
TestNG Spock 
言語 Java Groovy 
強力なAssert
12 
サンプルコード 
Gebで便利になること 
import geb.Browser 
Browser.drive { 
go http://myapp.com/login 
assert $(h1).text() == Please Login 
$(“p”, text:”hoge”) 
$(form.login).with { 
username = admin 
password = password 
login().click() 
} 
assert $(h1).text() == Admin Section 
} 
特定のURLを開いて 
見出しを確認 
フォームに入力してログイン 
見出しを確認
13 
Gebで便利になること 
サンプルコードをJJaavvaaWWeebbDDrriivveerrで書くと 
// importは省略 
WebDriver driver = new FirefoxDriver(); 
driver.get(“http://myapp.com/login”); 
assertThat(driver.findElement(By.tag(“h1”)).getText(), is(“Please Login”)); 
WebElement form = driver.findElement(By.cssSelector(“form.login”)); 
WebElement userName = form.findElement(By.name(“username”)); 
userName.clear(); 
userName.sendKeys(“admin”); 
WebElement password = form.findElement(By.name(“password”)); 
password.clear(); 
password.sendKeys(“password”); 
form.findElement(By.name(“login”)).click(); 
assertThat(driver.findElement(By.tag(“h1”)).getText(), is(“Admin Section”));
14 
Gebで便利になること 
メリット① 記述量の少なさ 
n コードが短くなるポイントがいくつもある 
import geb.Browser 
Browser.drive { 
go http://myapp.com/login 
assert $(h1).text() == Please Login 
$(form.login).with { 
username = admin 
password = password 
login().click() 
} 
assert $(h1).text() == Admin Section 
} 
いちいちdriver.**と 
書かなくてもOK 
jQueryライクな 
セレクタ 
form内の要素は 
さらに簡単に記述 
(name属性を使う)
15 
Gebで便利になること 
メリット② 「あるある」が解決済み 
n WebDriverの「あるある」独自改造が既に出来ている 
n テキスト入力のシンプル化 
n WebDriverインスタンスの生成処理の外出し 
n 要素を見つけるときの待ち時間の変更 
n テスト環境のURL
16 
Gebで便利になること 
メリット② 「あるある」が解決済み 
n テキスト入力のシンプル化 
// Javaの場合、テキスト入力が長くなって面倒なので 
WebElement element = driver.findElement(By.id(“hoge”)); 
element.clear(); 
element.sendKeys(“fuga”); 
// 1行で出来るようにする 
MyDriver.setText(By.id(“hoge”), “fuga”); 
// Gebの場合、そもそも1行で書ける 
$(“#hoge”).value(“fuga”) 
n 割愛しますがプルダウンなども同様
17 
Gebで便利になること 
メリット② 「あるある」が解決済み 
n 設定系はGebConfig.groovyに集約 
n クラスパス直下にこのファイル名で配置すると自動的に読み込まれる 
// WebDriverインスタンスの生成 
driver = { new FirefoxDriver() } 
// 要素を見つけるときの待ち時間 
waiting { 
timeout = 10 
retryInterval = 0.5 
} 
// テスト環境のURL 
baseUrl = ‘http://localhost:8000/example/’
18 
Gebで便利になること 
メリット③ もちろんPPaaggeeOObbjjeeccttもサポート 
class ExamplePage extends Page { 
// browser.toに対応 
static url = “http://www.example.com/” 
// browser.atに対応 
static at = { title = “Example Page” } 
// 操作したい各要素のlocatorを定義 
static content = { 
textUserName { $(“input[name=user_name]”) } 
textMailAddress { $(“input[name=mail_address]”) } 
} 
} 
Browser.drive { 
to ExamplePage // ←urlで指定したURLへ遷移 
/** 略 */ 
at ExamplePage // ←atの条件でassert 
}
Gebで便利になること 
メリット④ GGrroooovvyy//SSppoocckkの嬉しい機能が使える 
19 
n ブロックが使える 
n データ駆動テストがシンプルに書ける 
n モジュールが使える
Gebで便利になること 
メリット④ GGrroooovvyy//SSppoocckkの嬉しい機能が使える 
20 
n ブロックを使ってテストのフェーズを表現できる 
// テストの準備と後片付けの部分を明示 
class LoginSpec extends GebSpec { 
def “IDとPWの組み合わせが正しい場合にログインできる” { 
setup: 
to “http://www.example.com/login/” 
when: 
$(“#loginId”).value(“user01”) 
$(“#password”).value(“pass01”) 
$(“#login”).click() 
then: 
at http://www.example.com/home/ 
cleanup: 
// 
} 
}
Gebで便利になること 
メリット④ GGrroooovvyy//SSppoocckkの嬉しい機能が使える 
21 
n データ駆動テストがシンプルに書ける 
// 異なるID/PWでログイン結果を検証 
class LoginSpec extends GebSpec { 
def “IDとPWの組み合わせが正しくない場合ログインに失敗する” { 
when: 
$(“#loginId”).value(loginId) 
$(“#password”).value(password) 
$(“#login”).click() 
then: 
$(“#error”).text() == errorMessage 
where: 
loginId | password || errorMessage 
“” | “pass01” || “ログインIDを入力してください” 
“user01” | “” || “パスワードを入力してください” 
“hoge” | “fuga” || “IDとパスワードの組み合わせが正しくありません” 
} 
} 
↑入力と期待値の間は「||」で分ける
Gebで便利になること 
メリット④ GGrroooovvyy//SSppoocckkの嬉しい機能が使える 
22 
n モジュールが使える 
n サイト内の共通メニュー、ECサイトのカートなど複数の 
ページに共通する部品を表現するのに便利 
// 普通のPageObjectのような感覚でModuleを実装 
class CommonMenuModule extends Module { 
static content = { 
linkHome { $(“#home”) } 
linkLogout { $(“#logout”) } 
} 
} 
// PageObject内でフィールドとして扱う 
class ExamplePage extends Page { 
static content = { 
commonMenu { module CommonMenu } 
} 
}
23 
Gebで便利になること 
メリット⑤ JJaavvaaの資産が使える 
n GroovyはJavaの上位互換 
n Javaのクラスをそのままインポート可能 
n 原則、Javaの構文そのままでも記述できる 
n 日付処理や一意性確保等のために作ったユーティリティは 
引き続き使うことができる
24 
つまずくかもしれないこと 
SSyynnttaaxxの省略に注意 
n 省略は便利だが、場合によってはミスを招くこともある 
n IDE上での補完に頼りたければ、多少長くなっても型を明記 
// 省略あり 
to InputPage 
userName = “山田 太郎” 
mailAddress = “taro.yamada@example.com” 
// 省略なし 
InputPage page = browser.to InputPage 
page.userName = “山田 太郎” 
page.mailAddress = “taro.yamada@example.com” 
InputPageクラスのフィールド・メソッドが補完される
25 
つまずくかもしれないこと 
SSyynnttaaxxの省略に注意 
n Implicit Assertionの使い方に注意 
// assertを省略しても大丈夫なケース 
$(“p#message”).text() == “MESSAGE” 
waitFor { $(“p#message”).text() == “MESSAGE” } 
// assertを省略できないケース 
if (/** 条件 */) { 
assert $(“p#message”).text() == “MESSAGE” 
} 
n そもそも下の書き方(ケース内で分岐)は微妙 
n どうしても避けられないなら全部にassertを入れる
26 
つまずくかもしれないこと 
PPaaggeeOObbjjeeccttの作り方に注意 
n getter/setterを作らなくても動かせるので、ついつい生の 
処理を書いてしまいがち 
n PageObjectの意味がなくなってしまう 
// 悪い例 
【PageObject】 
static content = { 
textLoginId { $(“#login_id”) } 
textPassword { $(“#password”) } 
buttonLogin { $(“#login”) } 
} 
【TestCase】 
loginPage.textLoginId = “user01” 
loginPage.textPassword = “pass01” 
buttonLogin.click()
27 
つまずくかもしれないこと 
PPaaggeeOObbjjeeccttの作り方に注意 
n 保守性を意識してメソッドを切り分ける 
// 良い例 
【PageObject】 
static content = { /** 略 */ } 
void login(String loginId, String password) { 
textLoginId = loginId 
textPassword = password 
buttonLogin.click() 
} 
【TestCase】 
loginPage.login(“user01”, “pass01”)
28 
①失敗時のスクリーンショット取得 
+αの工夫 
n 前回も話題にした、個人的なこだわり 
n Spockのリスナ機能を使って実装 
// テスト開始時・終了時・失敗時等のフックを定義できる 
class MyListener extends AbstractRunListener { 
def void error(ErrorInfo error) { 
// スクリーンショットを撮って保存 
} 
} 
// 作成したリスナを登録する 
class GlobalSpecException implements IGlobalExtension { 
@Override 
void visitSpec(SpecInfo specInfo) { 
specInfo.addListener(new MyListener()) 
} 
}
29 
②TTeessttSSuuiitteeの動的生成 
+αの工夫 
n 自動テストを作り始めの不安定な頃は「Jenkinsで実行した 
ときだけテストが失敗する」みたいな事象もある 
n 失敗したケースだけを修正前後にサクッと実行できたら便利 
// TestSuiteのテンプレート 
@RunWith(Suite) 
@SuiteClasses([%TEST_CLASS%]) 
class AllSpecifications {} 
n Jenkinsのパラメタで%TEST_CLASS%を置換
30 
まとめ 
n Gebを使うとWebDriverのコードがシンプルになる 
n 独自改造も減らすことができる 
n Javaの人もそうでない人も 
n Gebを使ってみましょう!
31 
最後になりますが、 
SHIFTでは⾃自動化エンジニアを 
(これから始めたい⼈人も含めて) 
⼤大募集しております!!
32 
ご清聴ありがとうございました。

脱・独自改造! GebでWebDriverをもっとシンプルに

  • 1.
    1 第2回 日本Seleniumユーザコミュニティ勉強会 脱・独自改�造! GGeebbでWWeebbDDrriivveerrをもっとシンプルに 株式会社SHIFT 玉川紘子
  • 2.
    2 AAGGEENNDDAA n自己紹介+α n Gebで便利になること n つまずくかもしれないこと n 工夫してみたこと n まとめ
  • 3.
    3 自己紹介 名前:玉川紘子(たまがわ ひろこ) 所属:株式会社SHIFT ソフトウェアテスト事業本部    技術開発部 コミュニティ:STAR(テスト自動化研究会)        日本Jenkinsユーザ会
  • 4.
    4 CCII・自動テストなんでも屋さんとして活動中 開発言語:Java/PHP/Rubyなど 業務でよく使うツール:Jenkins/Selenium メイン業務はCI・自動テストに関するなんでもお手伝い 最近はJenkinsに関するセミナーなどもさせて頂いてます n 運用方針の提案 n 実際に稼働するCI環境の構築 n テストの書き方指南 Jenkinsってどうやって 使えばいいんだっけ? Seleniumで テストを書いてみたい んだけど…
  • 5.
    5 「実践 SSeelleenniiuummWWeebbDDrriivveerr」を 少しだけお手伝いしました n 翻訳+付録Bは玉川竜司さん n 私は付録Aとして、Seleniumと Jenkinsの連携について寄稿させて頂 きました ※竜司さんとは親戚ではありません 今回は書籍の発売記念を兼ねてということで、 竜司さんのおまけで声をかけていただきました。
  • 6.
  • 7.
  • 8.
    8 Gebのお話に行く前に WWeebbDDrriivveerr、そのままでは微妙ですよね n ちょっとしたことを書くにも、長い。。。 // テキスト入力 WebElement element = driver.findElement(By.name(“hoge”)); element.clear(); element.sendKeys(); // プルダウンの選択 Select select = new Select(driver.findElement(By.name(“fuga”))); select.selectByValue(“00”); n 大体みんな同じようなラッパーを作っている n と、前回の勉強会に出て思いました。
  • 9.
    9 Gebのお話に行く前に 「あるある」で共感している場合なのか n みんなで同じものを作るのは勿体ない n OSSで提供されているライブラリも色々 n FluentLenium(Java) n Geb(Groovy) n テスト対象製品に依存しないような改良は、独自に行うの ではなく既存ライブラリに頼ってしまおう
  • 10.
    10 GGeebbとは Gebで便利になること n 読み方は「じぇぶ」 n http://www.gebish.org/ n Groovyで動くWebDriverのラッパー n jQueryライクでわかりやすいLocator n コード量が少なく、すっきり書ける n テスト自動化研究会で@kyon_mmさんから教えてもらいま した
  • 11.
    11 Gebで便利になること ツールの立ち位置としてはこんな感じ クロスブラウザ対応 データ駆動テスト ブラウザ操作 WebDriver Geb テスティング フレームワーク JUnit TestNG Spock 言語 Java Groovy 強力なAssert
  • 12.
    12 サンプルコード Gebで便利になること import geb.Browser Browser.drive { go http://myapp.com/login assert $(h1).text() == Please Login $(“p”, text:”hoge”) $(form.login).with { username = admin password = password login().click() } assert $(h1).text() == Admin Section } 特定のURLを開いて 見出しを確認 フォームに入力してログイン 見出しを確認
  • 13.
    13 Gebで便利になること サンプルコードをJJaavvaaWWeebbDDrriivveerrで書くと // importは省略 WebDriver driver = new FirefoxDriver(); driver.get(“http://myapp.com/login”); assertThat(driver.findElement(By.tag(“h1”)).getText(), is(“Please Login”)); WebElement form = driver.findElement(By.cssSelector(“form.login”)); WebElement userName = form.findElement(By.name(“username”)); userName.clear(); userName.sendKeys(“admin”); WebElement password = form.findElement(By.name(“password”)); password.clear(); password.sendKeys(“password”); form.findElement(By.name(“login”)).click(); assertThat(driver.findElement(By.tag(“h1”)).getText(), is(“Admin Section”));
  • 14.
    14 Gebで便利になること メリット① 記述量の少なさ n コードが短くなるポイントがいくつもある import geb.Browser Browser.drive { go http://myapp.com/login assert $(h1).text() == Please Login $(form.login).with { username = admin password = password login().click() } assert $(h1).text() == Admin Section } いちいちdriver.**と 書かなくてもOK jQueryライクな セレクタ form内の要素は さらに簡単に記述 (name属性を使う)
  • 15.
    15 Gebで便利になること メリット② 「あるある」が解決済み n WebDriverの「あるある」独自改造が既に出来ている n テキスト入力のシンプル化 n WebDriverインスタンスの生成処理の外出し n 要素を見つけるときの待ち時間の変更 n テスト環境のURL
  • 16.
    16 Gebで便利になること メリット② 「あるある」が解決済み n テキスト入力のシンプル化 // Javaの場合、テキスト入力が長くなって面倒なので WebElement element = driver.findElement(By.id(“hoge”)); element.clear(); element.sendKeys(“fuga”); // 1行で出来るようにする MyDriver.setText(By.id(“hoge”), “fuga”); // Gebの場合、そもそも1行で書ける $(“#hoge”).value(“fuga”) n 割愛しますがプルダウンなども同様
  • 17.
    17 Gebで便利になること メリット② 「あるある」が解決済み n 設定系はGebConfig.groovyに集約 n クラスパス直下にこのファイル名で配置すると自動的に読み込まれる // WebDriverインスタンスの生成 driver = { new FirefoxDriver() } // 要素を見つけるときの待ち時間 waiting { timeout = 10 retryInterval = 0.5 } // テスト環境のURL baseUrl = ‘http://localhost:8000/example/’
  • 18.
    18 Gebで便利になること メリット③ もちろんPPaaggeeOObbjjeeccttもサポート class ExamplePage extends Page { // browser.toに対応 static url = “http://www.example.com/” // browser.atに対応 static at = { title = “Example Page” } // 操作したい各要素のlocatorを定義 static content = { textUserName { $(“input[name=user_name]”) } textMailAddress { $(“input[name=mail_address]”) } } } Browser.drive { to ExamplePage // ←urlで指定したURLへ遷移 /** 略 */ at ExamplePage // ←atの条件でassert }
  • 19.
    Gebで便利になること メリット④ GGrroooovvyy//SSppoocckkの嬉しい機能が使える 19 n ブロックが使える n データ駆動テストがシンプルに書ける n モジュールが使える
  • 20.
    Gebで便利になること メリット④ GGrroooovvyy//SSppoocckkの嬉しい機能が使える 20 n ブロックを使ってテストのフェーズを表現できる // テストの準備と後片付けの部分を明示 class LoginSpec extends GebSpec { def “IDとPWの組み合わせが正しい場合にログインできる” { setup: to “http://www.example.com/login/” when: $(“#loginId”).value(“user01”) $(“#password”).value(“pass01”) $(“#login”).click() then: at http://www.example.com/home/ cleanup: // } }
  • 21.
    Gebで便利になること メリット④ GGrroooovvyy//SSppoocckkの嬉しい機能が使える 21 n データ駆動テストがシンプルに書ける // 異なるID/PWでログイン結果を検証 class LoginSpec extends GebSpec { def “IDとPWの組み合わせが正しくない場合ログインに失敗する” { when: $(“#loginId”).value(loginId) $(“#password”).value(password) $(“#login”).click() then: $(“#error”).text() == errorMessage where: loginId | password || errorMessage “” | “pass01” || “ログインIDを入力してください” “user01” | “” || “パスワードを入力してください” “hoge” | “fuga” || “IDとパスワードの組み合わせが正しくありません” } } ↑入力と期待値の間は「||」で分ける
  • 22.
    Gebで便利になること メリット④ GGrroooovvyy//SSppoocckkの嬉しい機能が使える 22 n モジュールが使える n サイト内の共通メニュー、ECサイトのカートなど複数の ページに共通する部品を表現するのに便利 // 普通のPageObjectのような感覚でModuleを実装 class CommonMenuModule extends Module { static content = { linkHome { $(“#home”) } linkLogout { $(“#logout”) } } } // PageObject内でフィールドとして扱う class ExamplePage extends Page { static content = { commonMenu { module CommonMenu } } }
  • 23.
    23 Gebで便利になること メリット⑤ JJaavvaaの資産が使える n GroovyはJavaの上位互換 n Javaのクラスをそのままインポート可能 n 原則、Javaの構文そのままでも記述できる n 日付処理や一意性確保等のために作ったユーティリティは 引き続き使うことができる
  • 24.
    24 つまずくかもしれないこと SSyynnttaaxxの省略に注意 n 省略は便利だが、場合によってはミスを招くこともある n IDE上での補完に頼りたければ、多少長くなっても型を明記 // 省略あり to InputPage userName = “山田 太郎” mailAddress = “[email protected]” // 省略なし InputPage page = browser.to InputPage page.userName = “山田 太郎” page.mailAddress = “[email protected]” InputPageクラスのフィールド・メソッドが補完される
  • 25.
    25 つまずくかもしれないこと SSyynnttaaxxの省略に注意 n Implicit Assertionの使い方に注意 // assertを省略しても大丈夫なケース $(“p#message”).text() == “MESSAGE” waitFor { $(“p#message”).text() == “MESSAGE” } // assertを省略できないケース if (/** 条件 */) { assert $(“p#message”).text() == “MESSAGE” } n そもそも下の書き方(ケース内で分岐)は微妙 n どうしても避けられないなら全部にassertを入れる
  • 26.
    26 つまずくかもしれないこと PPaaggeeOObbjjeeccttの作り方に注意 n getter/setterを作らなくても動かせるので、ついつい生の 処理を書いてしまいがち n PageObjectの意味がなくなってしまう // 悪い例 【PageObject】 static content = { textLoginId { $(“#login_id”) } textPassword { $(“#password”) } buttonLogin { $(“#login”) } } 【TestCase】 loginPage.textLoginId = “user01” loginPage.textPassword = “pass01” buttonLogin.click()
  • 27.
    27 つまずくかもしれないこと PPaaggeeOObbjjeeccttの作り方に注意 n 保守性を意識してメソッドを切り分ける // 良い例 【PageObject】 static content = { /** 略 */ } void login(String loginId, String password) { textLoginId = loginId textPassword = password buttonLogin.click() } 【TestCase】 loginPage.login(“user01”, “pass01”)
  • 28.
    28 ①失敗時のスクリーンショット取得 +αの工夫 n 前回も話題にした、個人的なこだわり n Spockのリスナ機能を使って実装 // テスト開始時・終了時・失敗時等のフックを定義できる class MyListener extends AbstractRunListener { def void error(ErrorInfo error) { // スクリーンショットを撮って保存 } } // 作成したリスナを登録する class GlobalSpecException implements IGlobalExtension { @Override void visitSpec(SpecInfo specInfo) { specInfo.addListener(new MyListener()) } }
  • 29.
    29 ②TTeessttSSuuiitteeの動的生成 +αの工夫 n 自動テストを作り始めの不安定な頃は「Jenkinsで実行した ときだけテストが失敗する」みたいな事象もある n 失敗したケースだけを修正前後にサクッと実行できたら便利 // TestSuiteのテンプレート @RunWith(Suite) @SuiteClasses([%TEST_CLASS%]) class AllSpecifications {} n Jenkinsのパラメタで%TEST_CLASS%を置換
  • 30.
    30 まとめ nGebを使うとWebDriverのコードがシンプルになる n 独自改造も減らすことができる n Javaの人もそうでない人も n Gebを使ってみましょう!
  • 31.
    31 最後になりますが、 SHIFTでは⾃自動化エンジニアを (これから始めたい⼈人も含めて) ⼤大募集しております!!
  • 32.