32
1 脱・独自造! でÝをもっとシンプルに 株式会社SHIFT 玉川紘子 第2回 日本Seleniumユーザコミュニティ勉強会

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

Embed Size (px)

DESCRIPTION

Gebの入門的なお話をしました。

Citation preview

1

脱・独自改�造! GGeebbでWWeebbDDrriivveerrをもっとシンプルに

株式会社SHIFT玉川紘子

第2回  日本Seleniumユーザコミュニティ勉強会

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

WWeebbDDrriivveerr、そのままでは微妙ですよね

n ちょっとしたことを書くにも、長い。。。

n 大体みんな同じようなラッパーを作っている n と、前回の勉強会に出て思いました。

Gebのお話に行く前に

// テキスト入力 WebElement element = driver.findElement(By.name(“hoge”)); element.clear(); element.sendKeys(); // プルダウンの選択 Select select = new Select(driver.findElement(By.name(“fuga”))); select.selectByValue(“00”);

9

「あるある」で共感している場合なのか

n みんなで同じものを作るのは勿体ない n OSSで提供されているライブラリも色々

n  FluentLenium(Java) n  Geb(Groovy)

n テスト対象製品に依存しないような改良は、独自に行うのではなく既存ライブラリに頼ってしまおう

Gebのお話に行く前に

10

GGeebbとは

n 読み方は「じぇぶ」 n  http://www.gebish.org/ n  Groovyで動くWebDriverのラッパー n  jQueryライクでわかりやすいLocator n コード量が少なく、すっきり書ける

n テスト自動化研究会で@kyon_mmさんから教えてもらいました

Gebで便利になること

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

サンプルコードをJJaavvaa&&WWeebbDDrriivveerrで書くと

Gebで便利になること

// 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

メリット① 記述量の少なさ

n コードが短くなるポイントがいくつもある

Gebで便利になること

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

メリット② 「あるある」が解決済み

n WebDriverの「あるある」独自改造が既に出来ている n テキスト入力のシンプル化 n WebDriverインスタンスの生成処理の外出し n 要素を見つけるときの待ち時間の変更 n テスト環境のURL

Gebで便利になること

16

メリット② 「あるある」が解決済み

n テキスト入力のシンプル化

n 割愛しますがプルダウンなども同様

Gebで便利になること

// 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”)

17

メリット② 「あるある」が解決済み

n  設定系はGebConfig.groovyに集約 n  クラスパス直下にこのファイル名で配置すると自動的に読み込まれる

Gebで便利になること

// WebDriverインスタンスの生成 driver = { new FirefoxDriver() } // 要素を見つけるときの待ち時間 waiting { timeout = 10 retryInterval = 0.5 } // テスト環境のURL baseUrl = ‘http://localhost:8000/example/’

18

メリット③ もちろんPPaaggeeOObbjjeeccttもサポート

Gebで便利になること

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

メリット④ GGrroooovvyy//SSppoocckkの嬉しい機能が使える

n ブロックが使える n データ駆動テストがシンプルに書ける n モジュールが使える

Gebで便利になること

20

メリット④ GGrroooovvyy//SSppoocckkの嬉しい機能が使える

n ブロックを使ってテストのフェーズを表現できる

Gebで便利になること

// テストの準備と後片付けの部分を明示 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

メリット④ GGrroooovvyy//SSppoocckkの嬉しい機能が使える

n データ駆動テストがシンプルに書ける

Gebで便利になること

// 異なる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

メリット④ GGrroooovvyy//SSppoocckkの嬉しい機能が使える

n モジュールが使える n サイト内の共通メニュー、ECサイトのカートなど複数のページに共通する部品を表現するのに便利

Gebで便利になること

// 普通のPageObjectのような感覚でModuleを実装 class CommonMenuModule extends Module { static content = { linkHome { $(“#home”) } linkLogout { $(“#logout”) } } } // PageObject内でフィールドとして扱う class ExamplePage extends Page { static content = { commonMenu { module CommonMenu } } }

23

メリット⑤ JJaavvaaの資産が使える

n  GroovyはJavaの上位互換 n  Javaのクラスをそのままインポート可能 n 原則、Javaの構文そのままでも記述できる

n 日付処理や一意性確保等のために作ったユーティリティは引き続き使うことができる

Gebで便利になること

24

SSyynnttaaxxの省略に注意

n 省略は便利だが、場合によってはミスを招くこともある n  IDE上での補完に頼りたければ、多少長くなっても型を明記

つまずくかもしれないこと

// 省略あり to InputPage userName = “山田  太郎” mailAddress = “[email protected]” // 省略なし InputPage page = browser.to InputPage page.userName = “山田  太郎” page.mailAddress = “[email protected]

InputPageクラスのフィールド・メソッドが補完される

25

SSyynnttaaxxの省略に注意

n  Implicit Assertionの使い方に注意

n そもそも下の書き方(ケース内で分岐)は微妙 n どうしても避けられないなら全部にassertを入れる

つまずくかもしれないこと

// assertを省略しても大丈夫なケース $(“p#message”).text() == “MESSAGE” waitFor { $(“p#message”).text() == “MESSAGE” } // assertを省略できないケース if (/** 条件 */) { assert $(“p#message”).text() == “MESSAGE” }

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 失敗したケースだけを修正前後にサクッと実行できたら便利

n  Jenkinsのパラメタで%TEST_CLASS%を置換

+αの工夫

// TestSuiteのテンプレート @RunWith(Suite) @SuiteClasses([%TEST_CLASS%]) class AllSpecifications {}

30

まとめ

n  Gebを使うとWebDriverのコードがシンプルになる n 独自改造も減らすことができる n  Javaの人もそうでない人も

n  Gebを使ってみましょう!

31

最後になりますが、

SHIFTでは⾃自動化エンジニアを(これから始めたい⼈人も含めて)⼤大募集しております!!

32

ご清聴ありがとうございました。