157
What makes Geb groovy? @PoohSunny

What makes Geb groovy?

Embed Size (px)

Citation preview

What makes Geb groovy?@PoohSunny

こんにちは!

自己紹介の前にいくつか質問

質問1

Gebの利用経験

1. 知らなかった2. 知ってたけど「げぶ」って読むと思ってた3. インストールして、試しに動かしてみたことがある4. 仕事や趣味のプロジェクトで使っている5. むしろ作っている

質問2

普段の利用言語

質問3

WEBのGUIテスト自動化

● してますか?● どんな方法でしてますか?

○ Selenium IDE ?○ WebDriver ?

自己紹介

● @PoohSunny● 現在はカエルのマークのSaasスタートアップ

チームで奮闘中● Groovy, Grails, Gradle, Geb を使ってお仕事● さっきの質問への回答

○ Gebは仕事で使っています○ 今はGroovy, 前はJava○ Gebの前はWebDriver - Javaを使ってました

質問4

WebDriverって、好きですか?

● 書くと長い(長過ぎる)● driver.xxx とか書きたくない● みんな同じようなラッパーを作っているらしい

そこで

Geb (「じぇぶ」と発音します)

めっちゃgroovyなブラウザ操作自動化... WEB(のGUI)テスト、スクリーンキャプチャ、and more

※ groovyには「すばらしい」とか「イケてる」といった意味があります。

http://www.thefreedictionary.com/groovy

● http://www.gebish.org/● Selenium WebDriverのラッパー● Groovy製● 2015年1月時点でのバージョンは0.10.0● ライセンスはApache License Version 2.0

今日触れること

● Gebひとまわり(これが大半)○ Gebの良いところ洗いざらい

● はじめてみよう!○ とりあえずさくっと始めてみるには

質問はいつでもどうぞ!!

もしくは #gebjpつけて @PoohSunny にメンションください!

今日触れないこと

● 効果的なWEB GUIテストの書き方○ テスト戦略○ テスト計画○ 良いテストケースの作成方法 etc.

● Gebのハマりどころ○ 次回以降の発表に期待!

今日の話で

● 「おっ、Gebって良さそうじゃん。ちょっと手元で動かしてみようかな」

● 「試しに動かしてみた!」

となれば幸いです!!

でははじめましょう!

Geb’s Highlights(Gebのページより)

● Cross Browser● jQuery-like API● Page Objects● Asynchronous Pages● Testing● Build Integration

Geb’s Highlights(Gebのページより)

● Cross Browser(クロスブラウザ)● jQuery-like API(jQuery的なAPI)● Page Objects(ページオブジェクト)● Asynchronous Pages(非同期ページの対応)● Testing(テスティング)● Build Integration(ビルドツールとの統合)

最初にこの2つから

● Cross Browser(クロスブラウザ)● jQuery-like API(jQuery的なAPI)● Page Objects(ページオブジェクト)● Asynchronous Pages(非同期ページの対応)● Testing(テスティング)● Build Integration(ビルドツールとの統合)

要するに

楽だ!

百聞は一見にしかずということで

サンプルコードを見ながら体感してみま

しょう。

サンプルコード(WebDriver - Java)import org.junit.*; import org.openqa.selenium.By; import org.openqa.selenium.WebDriver; import org.openqa.selenium.WebElement; import org.openqa.selenium.firefox.FirefoxDriver; import org.openqa.selenium.support.ui.ExpectedCondition; import org.openqa.selenium.support.ui.WebDriverWait; import static org.hamcrest.MatcherAssert.*; import static org.hamcrest.CoreMatchers.*; /** * http://docs.seleniumhq.org/docs/03_webdriver.jsp#introducing-the-selenium-webdriver-api-by-example * をベースに一部改変 */ public class sampleTest { WebDriver driver; @Before public void setUp() { driver = new FirefoxDriver(); } @Test public void googleSuggestTest() { // Googleのページへ遷移 driver.get("http://www.google.com"); // 他のやり方として、以下のように記述することもできます // driver.navigate().to("http://www.google.com"); // nameからtext inputのエレメントを探す WebElement element = driver.findElement(By.name("q")); // 検索するために何か入力 element.sendKeys("Cheese!"); // フォームをサブミット。WebDriverはエレメントからフォームを見つけてくれます。 element.submit(); // ページのタイトルをチェック assertThat(driver.getTitle(), is("Google")); // Google検索はJavaScriptで動的に表示されます // ページがロードされるのを待ちます。タイムアウトは10秒 (new WebDriverWait(driver, 10)).until(new ExpectedCondition<Boolean>() { public Boolean apply(WebDriver d) { return d.getTitle().toLowerCase().startsWith("cheese!"); } }); // "Cheese!"から始まる文字が表示されているべき assertThat(driver.getTitle(), startsWith("Cheese!")); } @After public void tearDown() { // ブラウザを閉じる driver.quit(); } }

小さすぎて読めないので

● 基本省略します○ インポート○ クラス○ コメント

● 今日のコードの半分くらいはhttps://github.com/PoohSunny/geb-study に上げてあります。

サンプルコード(WebDriver - Java) WebDriver driver; @Before public void setUp() { driver = new FirefoxDriver(); } @Test public void googleSuggestTest() { driver.get("http://www.google.com");

WebElement element = driver.findElement(By.name("q")); element.sendKeys("Cheese!"); element.submit(); assertThat(driver.getTitle(), is("Google")); (new WebDriverWait(driver, 10)).until(new ExpectedCondition<Boolean>() { public Boolean apply(WebDriver d) { return d.getTitle().toLowerCase().startsWith("cheese!"); } }); assertThat(driver.getTitle(), startsWith("Cheese!")); } @After public void tearDown() { driver.quit(); }

下準備

import geb.junit4.GebReportingTest;

@RunWith(JUnit4)public class sampleTest extends GebReportingTest {

これで、JUnit4でGebが利用できます。(最初はJUnit4で実行

します。)

まずはDriverの記述を減らしましょう。

WebDriver to Geb WebDriver driver; @Before public void setUp() { driver = new FirefoxDriver(); } @Test public void googleSuggestTest() { driver.get("http://www.google.com");

WebElement element = driver.findElement(By.name("q")); element.sendKeys("Cheese!"); element.submit(); assertThat(driver.getTitle(), is("Google")); (new WebDriverWait(driver, 10)).until(new ExpectedCondition<Boolean>() { public Boolean apply(WebDriver d) { return d.getTitle().toLowerCase().startsWith("cheese!"); } }); assertThat(driver.getTitle(), startsWith("Cheese!")); } @After public void tearDown() { driver.quit(); }

new xxDriver() WebDriver driver; @Before public void setUp() { driver = new FirefoxDriver(); } @Test public void googleSuggestTest() { driver.get("http://www.google.com");

WebElement element = driver.findElement(By.name("q")); element.sendKeys("Cheese!"); element.submit(); assertThat(driver.getTitle(), is("Google")); (new WebDriverWait(driver, 10)).until(new ExpectedCondition<Boolean>() { public Boolean apply(WebDriver d) { return d.getTitle().toLowerCase().startsWith("cheese!"); } }); assertThat(driver.getTitle(), startsWith("Cheese!")); } @After public void tearDown() { driver.quit(); }

driverの記述は設定ファイル(GebConfig.groovy)内で

new xxDriver() WebDriver driver; @Before public void setUp() { } @Test public void googleSuggestTest() { driver.get("http://www.google.com");

WebElement element = driver.findElement(By.name("q")); element.sendKeys("Cheese!"); element.submit(); assertThat(driver.getTitle(), is("Google")); (new WebDriverWait(driver, 10)).until(new ExpectedCondition<Boolean>() { public Boolean apply(WebDriver d) { return d.getTitle().toLowerCase().startsWith("cheese!"); } }); assertThat(driver.getTitle(), startsWith("Cheese!")); } @After public void tearDown() { driver.quit(); }

driver.quit() WebDriver driver; @Before public void setUp() { } @Test public void googleSuggestTest() { driver.get("http://www.google.com");

WebElement element = driver.findElement(By.name("q")); element.sendKeys("Cheese!"); element.submit(); assertThat(driver.getTitle(), is("Google")); (new WebDriverWait(driver, 10)).until(new ExpectedCondition<Boolean>() { public Boolean apply(WebDriver d) { return d.getTitle().toLowerCase().startsWith("cheese!"); } }); assertThat(driver.getTitle(), startsWith("Cheese!")); } @After public void tearDown() { driver.quit(); }

quit()はGebが呼びます

driver.quit() WebDriver driver; @Before public void setUp() { } @Test public void googleSuggestTest() { driver.get("http://www.google.com");

WebElement element = driver.findElement(By.name("q")); element.sendKeys("Cheese!"); element.submit(); assertThat(driver.getTitle(), is("Google")); (new WebDriverWait(driver, 10)).until(new ExpectedCondition<Boolean>() { public Boolean apply(WebDriver d) { return d.getTitle().toLowerCase().startsWith("cheese!"); } }); assertThat(driver.getTitle(), startsWith("Cheese!")); } @After public void tearDown() { }

変数定義

WebDriver driver; @Before public void setUp() { } @Test public void googleSuggestTest() { driver.get("http://www.google.com");

WebElement element = driver.findElement(By.name("q")); element.sendKeys("Cheese!"); element.submit(); assertThat(driver.getTitle(), is("Google")); (new WebDriverWait(driver, 10)).until(new ExpectedCondition<Boolean>() { public Boolean apply(WebDriver d) { return d.getTitle().toLowerCase().startsWith("cheese!"); } }); assertThat(driver.getTitle(), startsWith("Cheese!")); } @After public void tearDown() { }

変数定義も不要

変数定義

@Before public void setUp() { } @Test public void googleSuggestTest() { driver.get("http://www.google.com");

WebElement element = driver.findElement(By.name("q")); element.sendKeys("Cheese!"); element.submit(); assertThat(driver.getTitle(), is("Google")); (new WebDriverWait(driver, 10)).until(new ExpectedCondition<Boolean>() { public Boolean apply(WebDriver d) { return d.getTitle().toLowerCase().startsWith("cheese!"); } }); assertThat(driver.getTitle(), startsWith("Cheese!")); } @After public void tearDown() { }

@Before, @After @Before public void setUp() { } @Test public void googleSuggestTest() { driver.get("http://www.google.com");

WebElement element = driver.findElement(By.name("q")); element.sendKeys("Cheese!"); element.submit(); assertThat(driver.getTitle(), is("Google")); (new WebDriverWait(driver, 10)).until(new ExpectedCondition<Boolean>() { public Boolean apply(WebDriver d) { return d.getTitle().toLowerCase().startsWith("cheese!"); } }); assertThat(driver.getTitle(), startsWith("Cheese!")); } @After public void tearDown() { }

初期処理が不要に

終了処理が不要に

@Before, @After @Test public void googleSuggestTest() { driver.get("http://www.google.com");

WebElement element = driver.findElement(By.name("q")); element.sendKeys("Cheese!"); element.submit(); assertThat(driver.getTitle(), is("Google")); (new WebDriverWait(driver, 10)).until(new ExpectedCondition<Boolean>() { public Boolean apply(WebDriver d) { return d.getTitle().toLowerCase().startsWith("cheese!"); } }); assertThat(driver.getTitle(), startsWith("Cheese!")); }

WebDriver to Geb @Test public void googleSuggestTest() { driver.get("http://www.google.com");

WebElement element = driver.findElement(By.name("q")); element.sendKeys("Cheese!"); element.submit(); assertThat(driver.getTitle(), is("Google")); (new WebDriverWait(driver, 10)).until(new ExpectedCondition<Boolean>() { public Boolean apply(WebDriver d) { return d.getTitle().toLowerCase().startsWith("cheese!"); } }); assertThat(driver.getTitle(), startsWith("Cheese!")); }

スペースができたのでコードを広げましょう

WebDriver to Geb

@Test public void googleSuggestTest() { driver.get("http://www.google.com");

WebElement element = driver.findElement(By.name("q")); element.sendKeys("Cheese!"); element.submit(); assertThat(driver.getTitle(), is("Google")); (new WebDriverWait(driver, 10)).until(new ExpectedCondition<Boolean>() { public Boolean apply(WebDriver d) { return d.getTitle().toLowerCase().startsWith("cheese!"); } }); assertThat(driver.getTitle(), startsWith("Cheese!")); }

Geb APIで簡潔に

WebDriver to Geb

@Test public void googleSuggestTest() { driver.get("http://www.google.com");

WebElement element = driver.findElement(By.name("q")); element.sendKeys("Cheese!"); assertThat(driver.getTitle(), is("Google"));

element.submit(); (new WebDriverWait(driver, 10)).until(new ExpectedCondition<Boolean>() { public Boolean apply(WebDriver d) { return d.getTitle().toLowerCase().startsWith("cheese!"); } }); assertThat(driver.getTitle(), startsWith("Cheese!")); }

driver.get()

@Test public void googleSuggestTest() { driver.get("http://www.google.com");

WebElement element = driver.findElement(By.name("q")); element.sendKeys("Cheese!"); assertThat(driver.getTitle(), is("Google"));

element.submit(); (new WebDriverWait(driver, 10)).until(new ExpectedCondition<Boolean>() { public Boolean apply(WebDriver d) { return d.getTitle().toLowerCase().startsWith("cheese!"); } }); assertThat(driver.getTitle(), startsWith("Cheese!")); }

リクエストの発行はgo メソッドで

go()

@Test public void googleSuggestTest() { go("http://www.google.com");

WebElement element = driver.findElement(By.name("q")); element.sendKeys("Cheese!"); assertThat(driver.getTitle(), is("Google")); element.submit(); (new WebDriverWait(driver, 10)).until(new ExpectedCondition<Boolean>() { public Boolean apply(WebDriver d) { return d.getTitle().toLowerCase().startsWith("cheese!"); } }); assertThat(driver.getTitle(), startsWith("Cheese!")); }

リクエストの発行はgo メソッドで

driver.findElement()

@Test public void googleSuggestTest() { go("http://www.google.com");

WebElement element = driver.findElement(By.name("q")); element.sendKeys("Cheese!"); assertThat(driver.getTitle(), is("Google"));

element.submit(); (new WebDriverWait(driver, 10)).until(new ExpectedCondition<Boolean>() { public Boolean apply(WebDriver d) { return d.getTitle().toLowerCase().startsWith("cheese!"); } }); assertThat(driver.getTitle(), startsWith("Cheese!")); }

jQueryっぽいAPIで書き換えましょう

$()

@Test public void googleSuggestTest() { go("http://www.google.com");

def element = $(input, name: “q”); element.sendKeys("Cheese!"); assertThat(driver.getTitle(), is("Google")); element.submit(); (new WebDriverWait(driver, 10)).until(new ExpectedCondition<Boolean>() { public Boolean apply(WebDriver d) { return d.getTitle().toLowerCase().startsWith("cheese!"); } }); assertThat(driver.getTitle(), startsWith("Cheese!")); }

jQueryっぽいAPIで書き換えましょう

jQuery的なAPI

● 正式にはNavigator APIって名前● ふじさわさんの発表で詳しく!● チートシート(日本語)

○ http://qiita.com/itagakishintaro/items/1fa06904bd0a6de73ee2

参考:http://www.gebish.org/content

element.sendKeys()

@Test public void googleSuggestTest() { go("http://www.google.com");

def element = $("input", name: "q"); element.sendKeys("Cheese!"); assertThat(driver.getTitle(), is("Google")); element.submit(); (new WebDriverWait(driver, 10)).until(new ExpectedCondition<Boolean>() { public Boolean apply(WebDriver d) { return d.getTitle().toLowerCase().startsWith("cheese!"); } }); assertThat(driver.getTitle(), startsWith("Cheese!")); }

sendKeysも書き換え

element.value()

@Test public void googleSuggestTest() { go("http://www.google.com");

def element = $("input", name: "q"); element.value("Cheese!"); assertThat(driver.getTitle(), is("Google")); element.submit(); (new WebDriverWait(driver, 10)).until(new ExpectedCondition<Boolean>() { public Boolean apply(WebDriver d) { return d.getTitle().toLowerCase().startsWith("cheese!"); } }); assertThat(driver.getTitle(), startsWith("Cheese!")); }

sendKeysも書き換え

<<

@Test public void googleSuggestTest() { go("http://www.google.com");

def element = $("input", name: "q"); element << "Cheese!"; assertThat(driver.getTitle(), is("Google")); element.submit(); (new WebDriverWait(driver, 10)).until(new ExpectedCondition<Boolean>() { public Boolean apply(WebDriver d) { return d.getTitle().toLowerCase().startsWith("cheese!"); } }); assertThat(driver.getTitle(), startsWith("Cheese!")); }

さらにこんな風にも書けます

driver.getTitle()

@Test public void googleSuggestTest() { go("http://www.google.com");

def element = $("input", name: "q"); element << "Cheese!"; assertThat(driver.getTitle(), is("Google")); element.submit(); (new WebDriverWait(driver, 10)).until(new ExpectedCondition<Boolean>() { public Boolean apply(WebDriver d) { return d.getTitle().toLowerCase().startsWith("cheese!"); } }); assertThat(driver.getTitle(), startsWith("Cheese!")); }

driverの記述は不要

title

@Test public void googleSuggestTest() { go("http://www.google.com");

def element = $("input", name: "q"); element << "Cheese!"; assertThat(title, is("Google")); element.submit(); (new WebDriverWait(driver, 10)).until(new ExpectedCondition<Boolean>() { public Boolean apply(WebDriver d) { return d.getTitle().toLowerCase().startsWith("cheese!"); } }); assertThat(driver.getTitle(), startsWith("Cheese!")); }

driverの記述は不要

Keys

@Test public void googleSuggestTest() { go("http://www.google.com");

def element = $("input", name: "q"); element << "Cheese!"; assertThat(title, is("Google")); element.submit(); (new WebDriverWait(driver, 10)).until(new ExpectedCondition<Boolean>() { public Boolean apply(WebDriver d) { return d.getTitle().toLowerCase().startsWith("cheese!"); } }); assertThat(driver.getTitle(), startsWith("Cheese!")); }

Seleniumのキーストロークもいけます

Keys

@Test public void googleSuggestTest() { go("http://www.google.com");

def element = $("input", name: "q"); element << "Cheese!"; assertThat(title, is("Google")); element << Keys.ENTER; (new WebDriverWait(driver, 10)).until(new ExpectedCondition<Boolean>() { public Boolean apply(WebDriver d) { return d.getTitle().toLowerCase().startsWith("cheese!"); } }); assertThat(driver.getTitle(), startsWith("Cheese!")); }

Seleniumのキーストロークもいけます

WebDriverWait()

@Test public void googleSuggestTest() { go("http://www.google.com");

def element = $("input", name: "q"); element << "Cheese!"; assertThat(title, is("Google")); element << Keys.ENTER; (new WebDriverWait(driver, 10)).until(new ExpectedCondition<Boolean>() { public Boolean apply(WebDriver d) { return d.getTitle().toLowerCase().startsWith("cheese!"); } }); assertThat(driver.getTitle(), startsWith("Cheese!")); }

waitは冗長になってしまいがち...

waitFor()

@Test public void googleSuggestTest() { go("http://www.google.com");

def element = $("input", name: "q"); element << "Cheese!"; assertThat(title, is("Google")); element << Keys.ENTER; waitFor { title.toLowerCase().startsWith("cheese!"); } assertThat(driver.getTitle(), startsWith("Cheese!")); }

Gebならスッキリ

非同期ページへの対応

● シンプルに書けるwait処理

参考:http://www.gebish.org/async

WebDriver to Geb

@Test public void googleSuggestTest() { go("http://www.google.com");

def element = $("input", name: "q"); element << "Cheese!"; assertThat(title, is("Google")); element << Keys.ENTER; waitFor { title.toLowerCase().startsWith("cheese!"); } assertThat(driver.getTitle(), startsWith("Cheese!")); }

driverの記述は不要(2回目)

WebDriver to Geb

@Test public void googleSuggestTest() { go("http://www.google.com");

def element = $("input", name: "q"); element << "Cheese!"; assertThat(title, is("Google")); element << Keys.ENTER; waitFor { title.toLowerCase().startsWith("cheese!"); } assertThat(title, startsWith("Cheese!")); }

driverの記述は不要(2回目)

WebDriver to Geb

@Test public void googleSuggestTest() { go("http://www.google.com");

def element = $("input", name: "q"); element << "Cheese!"; assertThat(title, is("Google")); element << Keys.ENTER; waitFor { title.toLowerCase().startsWith("cheese!"); } assertThat(title, startsWith("Cheese!")); }

余白を詰めてみます。

WebDriver to Geb

@Test public void googleSuggestTest() { go("http://www.google.com");

def element = $("input", name: "q"); element << "Cheese!"; assertThat(title, is("Google")); element << Keys.ENTER; waitFor { title.toLowerCase().startsWith("cheese!"); } assertThat(title, startsWith("Cheese!")); }

これが

WebDriver driver; @Before public void setUp() { driver = new FirefoxDriver(); } @Test public void googleSuggestTest() { driver.get("http://www.google.com");

WebElement element = driver.findElement(By.name("q")); element.sendKeys("Cheese!"); element.submit(); assertThat(driver.getTitle(), is("Google")); (new WebDriverWait(driver, 10)).until(new ExpectedCondition<Boolean>() { public Boolean apply(WebDriver d) { return d.getTitle().toLowerCase().startsWith("cheese!"); } }); assertThat(driver.getTitle(), startsWith("Cheese!")); } @After public void tearDown() { driver.quit(); }

こう

@Test public void googleSuggestTest() { go("http://www.google.com");

def element = $("input", name: "q"); element << "Cheese!"; assertThat(title, is("Google")); element << Keys.ENTER; waitFor { title.toLowerCase().startsWith("cheese!"); } assertThat(title, startsWith("Cheese!")); }

だいぶすっきりしました

でももう少し

groovyなコードに...

● JVM向けの動的型付き言語● ほとんどゼロの学習曲線で現代的なプログラミン

グ機能をJava開発者が利用できるようになる● 構文がコンパクトなので、読みやすくメンテナン

スしやすいコードになる● http://groovy-lang.org/● Groovyを一回りしたい人はこちら

○ https://speakerdeck.com/glaforge/what-makes-groovy-groovy

public

@Test public void googleSuggestTest() { go("http://www.google.com");

def element = $("input", name: "q"); element << "Cheese!"; assertThat(title, is("Google")); element << Keys.ENTER; waitFor { title.toLowerCase().startsWith("cheese!"); } assertThat(title, startsWith("Cheese!")); }

public の記述不要

public

@Test void googleSuggestTest() { go("http://www.google.com");

def element = $("input", name: "q"); element << "Cheese!"; assertThat(title, is("Google")); element << Keys.ENTER; waitFor { title.toLowerCase().startsWith("cheese!"); } assertThat(title, startsWith("Cheese!")); }

セミコロン

@Test void googleSuggestTest() { go("http://www.google.com");

def element = $("input", name: "q"); element << "Cheese!"; assertThat(title, is("Google")); element << Keys.ENTER; waitFor { title.toLowerCase().startsWith("cheese!"); } assertThat(title, startsWith("Cheese!")); }

セミコロン不要

セミコロン

@Test void googleSuggestTest() { go("http://www.google.com")

def element = $("input", name: "q") element << "Cheese!" assertThat(title, is("Google")) element << Keys.ENTER waitFor { title.toLowerCase().startsWith("cheese!") } assertThat(title, startsWith("Cheese!")) }

()

@Test void googleSuggestTest() { go("http://www.google.com")

def element = $("input", name: "q") element << "Cheese!" assertThat(title, is("Google")) element << Keys.ENTER waitFor { title.toLowerCase().startsWith("cheese!") } assertThat(title, startsWith("Cheese!")) }

パラメータがあれば() 不要

()

@Test void googleSuggestTest() { go "http://www.google.com"

def element = $("input", name: "q") element << "Cheese!" assertThat(title, is("Google")) element << Keys.ENTER waitFor { title.toLowerCase().startsWith("cheese!") } assertThat(title, startsWith("Cheese!")) }

assert

@Test void googleSuggestTest() { go "http://www.google.com"

def element = $("input", name: "q") element << "Cheese!" assertThat(title, is("Google")) element << Keys.ENTER waitFor { title.toLowerCase().startsWith("cheese!") } assertThat(title, startsWith("Cheese!")) }

JUnitのassertThatもassertに書き換えてしまいましょう!

assert

@Test void googleSuggestTest() { go "http://www.google.com"

def element = $("input", name: "q") element << "Cheese!" assert title == "Google" element << Keys.ENTER waitFor { title.toLowerCase().startsWith("cheese!") } assert title.startsWith("Cheese!") }

短くなっただけでなく、別なメリットも

パワーアサートが使える!

というわけで、これが

WebDriver driver; @Before public void setUp() { driver = new FirefoxDriver(); } @Test public void googleSuggestTest() { driver.get("http://www.google.com");

WebElement element = driver.findElement(By.name("q")); element.sendKeys("Cheese!"); element.submit(); assertThat(driver.getTitle(), is("Google")); (new WebDriverWait(driver, 10)).until(new ExpectedCondition<Boolean>() { public Boolean apply(WebDriver d) { return d.getTitle().toLowerCase().startsWith("cheese!"); } }); assertThat(driver.getTitle(), startsWith("Cheese!")); } @After public void tearDown() { driver.quit(); }

こう

@Test void googleSuggestTest() { go "http://www.google.com"

def element = $("input", name: "q") element << "Cheese!" assert title == "Google" element << Keys.ENTER waitFor { title.toLowerCase().startsWith("cheese!") } assert title.startsWith("Cheese!") }

これが

WebDriver driver; @Before public void setUp() { driver = new FirefoxDriver(); } @Test public void googleSuggestTest() { driver.get("http://www.google.com");

WebElement element = driver.findElement(By.name("q")); element.sendKeys("Cheese!"); element.submit(); assertThat(driver.getTitle(), is("Google")); (new WebDriverWait(driver, 10)).until(new ExpectedCondition<Boolean>() { public Boolean apply(WebDriver d) { return d.getTitle().toLowerCase().startsWith("cheese!"); } }); assertThat(driver.getTitle(), startsWith("Cheese!")); } @After public void tearDown() { driver.quit(); }

こう

@Test void googleSuggestTest() { go "http://www.google.com"

def element = $("input", name: "q") element << "Cheese!" assert title == "Google" element << Keys.ENTER waitFor { title.toLowerCase().startsWith("cheese!") } assert title.startsWith("Cheese!") }

ここまでいかがです?

けっこう簡潔になったでしょ?

忘れてました

画面キャプチャの取得の話

GebReportingSpec, GebReportingTestを継承

スクリーンキャプチャの取得

@Test void googleSuggestTest() { go "http://www.google.com"

def element = $("input", name: "q") element << "Cheese!" assert title == "Google" element << Keys.ENTER

report("検索ページ") waitFor { title.toLowerCase().startsWith("cheese!") } assert title.startsWith("Cheese!") }

report を呼び出すと、画面のHTML, およびキャプチャを取得。

参考:http://qiita.com/nyasba/items/edf102578bde7edf0d4f

スクリーンキャプチャの取得

@Test void googleSuggestTest() { go "http://www.google.com"

def element = $("input", name: "q") element << "Cheese!" assert title == "Google" element << Keys.ENTER

report("検索ページ") waitFor { title.toLowerCase().startsWith("cheese!") } assert title.startsWith("Cheese!") }

各テストケース終了時のキャプチャは、明示的に書かなくても自動取得

参考:http://qiita.com/nyasba/items/edf102578bde7edf0d4f

ここまでの話

● Cross Browser(クロスブラウザ)● jQuery-like API(jQuery的なAPI)● Page Objects(ページオブジェクト)● Asynchronous Pages(非同期ページの対応)● Testing(テスティング)● Build Integration(ビルドツールとの統合)● Screen Scraping(スクリーンキャプチャ)

次の話

● Cross Browser(クロスブラウザ)● jQuery-like API(jQuery的なAPI)● Page Objects(ページオブジェクト)● Asynchronous Pages(非同期ページの対応)● Testing(テスティング)● Build Integration(ビルドツールとの統合)● Screen Scraping(スクリーンキャプチャ)

ところで、このコード

@Test void googleSuggestTest() { go "http://www.google.com"

def element = $("input", name: "q") element << "Cheese!" assert title == "Google" element << Keys.ENTER waitFor { title.toLowerCase().startsWith("cheese!") } assert title.startsWith("Cheese!") }

もはやJUnitで動かすいみがほとんどない

Gebのページを読むと

GebはSpock, JUnit, TestNG, さらには(Cuke4Dukeを用いての)Cucumber といった、有名なテスティングフレームワークへの統合モジュールを提供しています。

参考:http://www.gebish.org/testing

Gebのページを読むと

GebはSpock, JUnit, TestNG, さらには(Cuke4Dukeを用いての)Cucumber といった、有名なテスティングフレームワークへの統合モジュールを提供しています。

参考:http://www.gebish.org/testing

I’m Spock

http://en.wikipedia.org/wiki/File:Leonard_Nimoy_as_Spock_1967.jpg

お待たせしました!

Spockで書くと

def "Googleで「Cheese!」を検索する"() { given: 'Googleを表示する' go "http://www.google.com" when: '検索欄にCheese!と入力して def element = $("input", name: "q") element << "Cheese!" and: '検索を実行する' element << Keys.ENTER waitFor { title.toLowerCase().startsWith("cheese!") } then: '検索結果のページを表示する"' title.startsWith("Cheese!") }

テストメソッド名がString(句読点とか入れられる)

Spockで書くと

def "Googleで「Cheese!」を検索する"() { given: 'Googleを表示する' go "http://www.google.com" when: '検索欄にCheese!と入力して def element = $("input", name: "q") element << "Cheese!" and: '検索を実行する' element << Keys.ENTER waitFor { title.toLowerCase().startsWith("cheese!") } then: '検索結果のページを表示する' title.startsWith("Cheese!") }

ブロックをつかってフェーズを表現できる

参考:https://code.google.com/p/spock/wiki/SpockBasics#Blocks

Spockで書くと

def "Googleで「Cheese!」を検索する"() { given: 'Googleを表示する' go "http://www.google.com" when: '検索欄にCheese!と入力して' def element = $("input", name: "q") element << "Cheese!" and: '検索を実行する' element << Keys.ENTER waitFor { title.toLowerCase().startsWith("cheese!") } then: '検索結果のページを表示する' title.startsWith("Cheese!") }

そのブロック内で何をしたいのかも書けます

参考:https://code.google.com/p/spock/wiki/SpockBasics#Blocks

他にもあるSpockの便利機能

whereをつかってデータ駆動テスト

@Unroll def " '#loginId' , '#password' でログインしようとすると '#error' と表示する"() { when: 'ログインしようとすると' $("#loginId").value(loginId) $("#password").value(password) $("#login-btn").click() then: 'エラーメッセージが表示される' $("#error-msg").text() == error

where: loginId | password || error "" | "aaa" || "ログインIDを入力してください。" "001 | "" || "パスワードを入力してください。" "001 | "aaa" || "該当するユーザーが見つかりませんでした。" }

http://www.slideshare.net/hirokotamagawa/20141018-2selenium-40423299 をベースにしています

whereをつかってデータ駆動テスト

@Unroll def " '#loginId' , '#password' でログインしようとすると '#error' と表示する"() { when: 'ログインしようとすると' $("#loginId").value(loginId) $("#password").value(password) $("#login-btn").click() then: 'エラーメッセージが表示される' $("#error-msg").text() == error

where: loginId | password || error "" | "aaa" || "ログインIDを入力してください。" "001 | "" || "パスワードを入力してください。" "001 | "aaa" || "該当するユーザーが見つかりませんでした。" }

http://www.slideshare.net/hirokotamagawa/20141018-2selenium-40423299 をベースにしています

テーブル記法でパラメーターを定義できる

whereをつかってデータ駆動テスト

@Unroll def " '#loginId' , '#password' でログインしようとすると '#error' と表示する"() { when: 'ログインしようとすると' $("#loginId").value(loginId) $("#password").value(password) $("#login-btn").click() then: 'エラーメッセージが表示される' $("#error-msg").text() == error

where: loginId | password || error "" | "aaa" || "ログインIDを入力してください。" "001" | "" || "パスワードを入力してください。" "001" | "aaa" || "該当するユーザーが見つかりませんでした。" }

http://www.slideshare.net/hirokotamagawa/20141018-2selenium-40423299 をベースにしています

たとえば3つめのデータでテストが失敗すると、「'001', 'aaa' でログインしようとすると '該当するユーザーが見つかりませんでした。' と表示する」と補完してくれます。

ここまでの話

● Cross Browser(クロスブラウザ)● jQuery-like API(jQuery的なAPI)● Page Objects(ページオブジェクト)● Asynchronous Pages(非同期ページの対応)● Testing(テスティング)● Build Integration(ビルドツールとの統合)● Screen Scraping(スクリーンキャプチャ)

次の話

● Cross Browser(クロスブラウザ)● jQuery-like API(jQuery的なAPI)● Page Objects(ページオブジェクト)● Asynchronous Pages(非同期ページの対応)● Testing(テスティング)● Build Integration(ビルドツールとの統合)● Screen Scraping(スクリーンキャプチャ)

クロスブラウザの自動化

● サポート○ Firefox○ Internet Explorer○ Google Chrome○ Opera○ Remote Browsers○ Headless Browsers

■ HTMLUnit■ PhantomJS

● 実験的にサポート○ Chrome on Android○ Safari on iPhone & iPad

設定も簡単

environments { chrome { driver = { new ChromeDriver() } } firefox { driver = { new FirefoxDriver() } } phantomJs { driver = { new PhantomJSDriver() } } }

※システムプロパティに ”geb.env”という名前でパラメータをセットする

たとえばgradleで

ext { // driverの指定

drivers = ["firefox", "chrome", "phantomJs"]}

drivers.each { driver -> task "${driver}Test"(type: Test) { systemProperty "geb.build.reportsDir", reporting.file("$name/geb") systemProperty "geb.env", driver } }

chromeTest { // 省略

}

他にもこんなツールと

Apache Software Foundation

最後の話

● Cross Browser(クロスブラウザ)● jQuery-like API(jQuery的なAPI)● Page Objects(ページオブジェクト)● Asynchronous Pages(非同期ページの対応)● Testing(テスティング)● Build Integration(ビルドツールとの統合)● Screen Scraping(スクリーンキャプチャ)

ページオブジェクト

● Selenium界隈で広く利用されているデザインパターン

● テスト対象のページをオブジェクトとしてテストから切り離し、メンテナンス性を高める

● 参考URL○ http://codezine.jp/article/detail/7527○ http://garbagetown.hatenablog.

com/entry/2013/11/07/075011

ページオブジェクトパターンの要約

● public メソッドは、そのページが提供するサービスを表現します

● ページの内部を露出しないようにします● 一般的に、アサーションは行いません● メソッドは別の PageObjects を返します● ページのすべてを表現する必要はありません● 同じアクションの異なる結果は、異なるメソッドと

してモデル化されます

※http://garbagetown.hatenablog.com/entry/2013/11/07/075011から引用

ページオブジェクトを標準サポート

class SignInPage extends Page {

static url = "http://www.application.com/" static at = { title == "Please sign in." } static content = { signInButton(wait: true) { $("button", 0, class: "btn btn-primary") } errorMessageBox { $("div", 0, class: "alert alert-warning")} header { module Header } signIn(to: MainPage, toWait: true) { username, password -> $("#username").value(username) $("#password").value(password) signInButton.click() } } }

Page

class SignInPage extends Page {

static url = "http://www.application.com/" static at = { title == "Please sign in." } static content = { signInButton(wait: true) { $("button", 0, class: "btn btn-primary") } errorMessageBox { $("div", 0, class: "alert alert-warning")} header { module Header } signIn(to: MainPage, toWait: true) { username, password -> $("#username").value(username) $("#password").value(password) signInButton.click() } } }

Pageクラスを継承

url

class SignInPage extends Page {

static url = "http://www.application.com/" static at = { title == "Please sign in." } static content = { signInButton(wait: true) { $("button", 0, class: "btn btn-primary") } errorMessageBox { $("div", 0, class: "alert alert-warning")} header { module Header } signIn(to: MainPage, toWait: true) { username, password -> $("#username").value(username) $("#password").value(password) signInButton.click() } } }

to SignInPage もしくはvia SignInPageと記述するとこのURLに遷移

at

class SignInPage extends Page {

static url = "http://www.application.com/" static at = { title == "Please sign in." } static content = { signInButton(wait: true) { $("button", 0, class: "btn btn-primary") } errorMessageBox { $("div", 0, class: "alert alert-warning")} header { module Header } signIn(to: MainPage, toWait: true) { username, password -> $("#username").value(username) $("#password").value(password) signInButton.click() } } }

at SignInPageと記述するとチェックが走る

ページ遷移にまつわるあれこれ

class SignInPage extends Page {

static url = "http://www.application.com/" static at = { title == "Please sign in." } static content = { signInButton(wait: true) { $("button", 0, class: "btn btn-primary") } errorMessageBox { $("div", 0, class: "alert alert-warning")} header { module Header } signIn(to: MainPage, toWait: true) { username, password -> $("#username").value(username) $("#password").value(password) signInButton.click() } } }

このあたりは後でもう少し詳しく触れます

content

class SignInPage extends Page {

static url = "http://www.application.com/" static at = { title == "Please sign in." } static content = { signInButton(wait: true) { $("button", 0, class: "btn btn-primary") } errorMessageBox { $("div", 0, class: "alert alert-warning")} header { module Header } signIn(to: MainPage, toWait: true) { username, password -> $("#username").value(username) $("#password").value(password) signInButton.click() } } }

ページ内のコンテンツはcontent ブロック内に

contentのwait

class SignInPage extends Page {

static url = "http://www.application.com/" static at = { title == "Please sign in." } static content = { signInButton(wait: true) { $("button", 0, class: "btn btn-primary") } errorMessageBox { $("div", 0, class: "alert alert-warning")} header { module Header } signIn(to: MainPage, toWait: true) { username, password -> $("#username").value(username) $("#password").value(password) signInButton.click() } } }

(wait: xx)と書くとコンテンツ取得時にwaitしてくれます。

module

class SignInPage extends Page {

static url = "http://www.application.com/" static at = { title == "Please sign in." } static content = { signInButton(wait: true) { $("button", 0, class: "btn btn-primary") } errorMessageBox { $("div", 0, class: "alert alert-warning")} header { module Header } signIn(to: MainPage, toWait: true) { username, password -> $("#username").value(username) $("#password").value(password) signInButton.click() } } }

複数ページで共通するコンポーネントは moduleとしてまとめられる

content内に処理

class SignInPage extends Page {

static url = "http://www.application.com/" static at = { title == "Please sign in." } static content = { signInButton(wait: true) { $("button", 0, class: "btn btn-primary") } errorMessageBox { $("div", 0, class: "alert alert-warning")} header { module Header } signIn(to: MainPage, toWait: true) { username, password -> $("#username").value(username) $("#password").value(password) signInButton.click() } } }

処理も書けます

to: xxPage

class SignInPage extends Page {

static url = "http://www.application.com/" static at = { title == "Please sign in." } static content = { signInButton(wait: true) { $("button", 0, class: "btn btn-primary") } errorMessageBox { $("div", 0, class: "alert alert-warning")} header { module Header } signIn(to: MainPage, toWait: true) { username, password -> $("#username").value(username) $("#password").value(password) signInButton.click() } } }

処理を呼び出した後の遷移先を指定

toWait: xx

class SignInPage extends Page {

static url = "http://www.application.com/" static at = { title == "Please sign in." } static content = { signInButton(wait: true) { $("button", 0, class: "btn btn-primary") } errorMessageBox { $("div", 0, class: "alert alert-warning")} header { module Header } signIn(to: MainPage, toWait: true) { username, password -> $("#username").value(username) $("#password").value(password) signInButton.click() } } }

ページ遷移が完了するまで待ってくれる

Pageを利用するテストケース

class WhenCheckNotifyOnTheDashborad extends GebSpec { def "ログインしてメインページで3件の新着通知を確認する"() { when: "サインインページを表示して" to SignInPage at SignInPage and: "ユーザー名とパスワードを入力してログインすると" signIn "SAMPLE_USER", "SAMPLE_PASSWORD"

then: "メインページを表示する" at MainPage when: "ページ内のダッシュボードを確認すると" // ...略 }

to

class WhenCheckNotifyOnTheDashborad extends GebSpec { def "ログインしてメインページで3件の新着通知を確認する"() { when: "サインインページを表示して" to SignInPage at SignInPage and: "ユーザー名とパスワードを入力してログインすると" signIn "SAMPLE_USER", "SAMPLE_PASSWORD"

then: "メインページを表示する" at MainPage when: "ページ内のダッシュボードを確認すると" // ...略 }

SingInPageへ遷移 &ページオブジェクトを移動

at

class WhenCheckNotifyOnTheDashborad extends GebSpec { def "ログインしてメインページで3件の新着通知を確認する"() { when: "サインインページを表示して" to SignInPage at SignInPage and: "ユーザー名とパスワードを入力してログインすると" signIn "SAMPLE_USER", "SAMPLE_PASSWORD"

then: "メインページを表示する" at MainPage when: "ページ内のダッシュボードを確認すると" // ...略 }

to でページ遷移すると at チェックがされるので、この例では不要

contentの呼び出し

class WhenCheckNotifyOnTheDashborad extends GebSpec { def "ログインしてメインページで3件の新着通知を確認する"() { when: "サインインページを表示して" to SignInPage at SignInPage and: "ユーザー名とパスワードを入力してログインすると" signIn "SAMPLE_USER", "SAMPLE_PASSWORD"

then: "メインページを表示する" at MainPage when: "ページ内のダッシュボードを確認すると" // ...略 }

SingInPage 内の処理の呼び出し

ページ遷移

class WhenCheckNotifyOnTheDashborad extends GebSpec { def "ログインしてメインページで3件の新着通知を確認する"() { when: "サインインページを表示して" to SignInPage at SignInPage and: "ユーザー名とパスワードを入力してログインすると" signIn "SAMPLE_USER", "SAMPLE_PASSWORD"

then: "メインページを表示する" at MainPage when: "ページ内のダッシュボードを確認すると" // ...略 }

この処理内で MainPageへの遷移 + ページオブジェクトの変更

at

class WhenCheckNotifyOnTheDashborad extends GebSpec { def "ログインしてメインページで3件の新着通知を確認する"() { when: "サインインページを表示して" to SignInPage at SignInPage and: "ユーザー名とパスワードを入力してログインすると" signIn "SAMPLE_USER", "SAMPLE_PASSWORD"

then: "メインページを表示する" at MainPage when: "ページ内のダッシュボードを確認すると" // ...略 }

次のページの at チェックを実行

別な書き方

class WhenCheckNotifyOnTheDashborad extends GebSpec { def "ログインしてメインページで3件の新着通知を確認する"() { when: "サインインページを表示して" SignInPage signInPage = browser.to SignInPage browser.at SignInPage and: "ユーザー名とパスワードを入力してログインすると" MainPage mainPage = signInPage.signIn "SAMPLE_USER" // 略 then: "メインページを表示する" browser.at MainPage when: "ページ内のダッシュボードを確認すると" // ...略 }

参考:http://www.slideshare.net/hirokotamagawa/20141018-2selenium-40423299 http://www.gebish.org/manual /current/ide-and-typing.html#strong_typing

ページを明示的に記述

class WhenCheckNotifyOnTheDashborad extends GebSpec { def "ログインしてメインページで3件の新着通知を確認する"() { when: "サインインページを表示して" SignInPage signInPage = browser.to SignInPage browser.at SignInPage and: "ユーザー名とパスワードを入力してログインすると" MainPage mainPage = signInPage.signIn "SAMPLE_USER" // 略 then: "メインページを表示する" browser.at MainPage when: "ページ内のダッシュボードを確認すると" // ...略 }

省略をやめる(Page内も書き換え必要)

参考:http://www.slideshare.net/hirokotamagawa/20141018-2selenium-40423299 http://www.gebish.org/manual /current/ide-and-typing.html#strong_typing

IDEで補完が効く

class WhenCheckNotifyOnTheDashborad extends GebSpec { def "ログインしてメインページで3件の新着通知を確認する"() { when: "サインインページを表示して" SignInPage signInPage = browser.to SignInPage browser.at SignInPage and: "ユーザー名とパスワードを入力してログインすると" MainPage mainPage = signInPage.signIn "SAMPLE_USER" // 略 then: "メインページを表示する" browser.at MainPage when: "ページ内のダッシュボードを確認すると" // ...略 }

IDEでメソッド名などの補完が効く

参考:http://www.slideshare.net/hirokotamagawa/20141018-2selenium-40423299 http://www.gebish.org/manual /current/ide-and-typing.html#strong_typing

補足:ページ遷移にまつわるAPI

リクエストを作るGebのAPI群● to()● via()● go()

補足:ページ遷移にまつわるAPI

● to()● via()-----------------まずはここの違い

● go()

go()

class GoogleSpec extends GebSpec { def "goメソッドはページオブジェクトのセットをしない"() { given: Page oldPage = page when: go "http://google.com" then: oldPage == page driver.currentUrl == "http://google.com" } }

http://www.gebish.org/manual/current/all.html#directをベースにしています

go()

class GoogleSpec extends GebSpec { def "goメソッドはページオブジェクトのセットをしない"() { given: Page oldPage = page when: go "http://google.com" then: oldPage == page driver.currentUrl == "http://google.com" } }

http://www.gebish.org/manual/current/all.html#directをベースにしています

元々oldPageがセットされていて

go()

class GoogleSpec extends GebSpec { def "goメソッドはページオブジェクトのセットをしない"() { given: Page oldPage = page when: go "http://google.com" then: oldPage == page driver.currentUrl == "http://google.com" } }

http://www.gebish.org/manual/current/all.html#directをベースにしています

goメソッドを呼んでも

go()

class GoogleSpec extends GebSpec { def "goメソッドはページオブジェクトのセットをしない"() { given: Page oldPage = page when: go "http://google.com" then: oldPage == page driver.currentUrl == "http://google.com" } }

http://www.gebish.org/manual/current/all.html#directをベースにしています

ページオブジェクトはoldPageのまま

go()

class GoogleSpec extends GebSpec { def "goメソッドはページオブジェクトのセットをしない"() { given: Page oldPage = page when: go "http://google.com" then: oldPage == page driver.currentUrl == "http://google.com" } }

http://www.gebish.org/manual/current/all.html#directをベースにしています

でもcurrentUrlは変わる

to() or via()

class GoogleSpec extends GebSpec { def "toメソッドはページオブジェクトをセットし、現在のurlも変更"() { given: Page oldPage = page when: to GoogleHomePage then: oldPage != page driver.currentUrl == "http://google.com" } }

http://www.gebish.org/manual/current/all.html#directをベースにしています

to() or via()

class GoogleSpec extends GebSpec { def "toメソッドはページオブジェクトをセットし、現在のurlも変更"() { given: Page oldPage = page when: to GoogleHomePage then: oldPage != page driver.currentUrl == "http://google.com" } }

http://www.gebish.org/manual/current/all.html#directをベースにしています

元々oldPageがセットされている

to() or via()

class GoogleSpec extends GebSpec { def "toメソッドはページオブジェクトをセットし、現在のurlも変更"() { given: Page oldPage = page when: to GoogleHomePage then: oldPage != page driver.currentUrl == "http://google.com" } }

http://www.gebish.org/manual/current/all.html#directをベースにしています

toメソッドを呼び出すと

to() or via()

class GoogleSpec extends GebSpec { def "toメソッドはページオブジェクトをセットし、現在のurlも変更"() { given: Page oldPage = page when: to GoogleHomePage then: oldPage != page driver.currentUrl == "http://google.com" } }

http://www.gebish.org/manual/current/all.html#directをベースにしています

ページオブジェクトが変わる(GoogleHomePageがセットされる)

to() or via()

class GoogleSpec extends GebSpec { def "toメソッドはページオブジェクトをセットし、現在のurlも変更"() { given: Page oldPage = page when: to GoogleHomePage then: oldPage != page driver.currentUrl == "http://google.com" } }

http://www.gebish.org/manual/current/all.html#directをベースにしています

currentUrlも変わる

to()とvia()の違い

● メソッド呼び出し後に atチェックが走るか○ to() => 走る○ via() => 走らない

■ 遷移先ページでリダイレクトがあるような場合はvia()を使う

● to() か、at チェックを組み合わせた via() をいつも使うのが良いアイデア

参考:http://www.gebish.org/manual/current/all.html#at_checking

to(),via(),go()まとめ

メソッド呼び出し後にページオブジェクトをセットするか

呼び出し後にatチェックをするか

to() する する

via() する しない

go() しない しない

いかがでした?

興味出てきました?

でも始めるのは難しそう?

とりあえずさくっと体験してみたい方

● オススメ体験法○ https://github.com/geb/geb-example-gradle○ cloneして、gradlew コマンドで実行○ たとえばmacなら、./gradlew chromeTest で実行○ こちらもApache Licence Version 2.0

■ https://github.com/geb 配下のプロジェクトは全

てApache Licence Version 2.0だそうです。

● 参考:http://markmail.org/search/?q=list%3Aorg.codehaus.

geb.user#query:list%3Aorg.codehaus.geb.user+page:1+mid:dyp256xnjaku7guq+state:results

まとめ

What makes Geb groovy?

クロスブラウザの自動化

● サポート○ Firefox○ Internet Explorer○ Google Chrome○ Opera○ Remote Browsers○ Headless Browsers

■ HTMLUnit■ PhantomJS

● 実験的にサポート○ Chrome on Android○ Safari on iPhone & iPad

jQuery的なAPI

参考:http://www.gebish.org/content

ページオブジェクトのサポート

class SignInPage extends Page {

static url = "http://www.application.com/" static at = { title == "Please sign in." } static content = { signInButton(wait: true) { $("button", 0, class: "btn btn-primary") } errorMessageBox { $("div", 0, class: "alert alert-warning")} header { module Header } signIn(to: MainPage, toWait: true) { username, password -> $("#username").value(username) $("#password").value(password) signInButton.click() }

class WhenCheckNotifyOnTheDashborad extends GebSpec { def "ログインしてメインページで3件の新着通知を確認する"() { when: "サインインページを表示して" to SignInPage at SignInPage and: "ユーザー名とパスワードを入力してログインすると" signIn "SAMPLE_USER", "SAMPLE_PASSWORD"

then: "メインページを表示する" at MainPage

非同期ページへの対応

● シンプルに書けるwait処理

参考:http://www.gebish.org/async

テスティング

GebはSpock, JUnit, TestNG, さらには(Cuke4Dukeを用いての)Cucumber といった、有名なテスティングフレームワークへの統合モジュールを提供しています。

参考:http://www.gebish.org/testing

他ツールとの統合

Apache Software Foundation

スクリーンキャプチャ

@Test void googleSuggestTest() { go "http://www.google.com"

def element = $("input", name: "q") element << "Cheese!" assert title == "Google" element << Keys.ENTER

report("検索ページ") waitFor { title.toLowerCase().startsWith("cheese!") } assert title.startsWith("Cheese!") }

report を呼び出すと、画面のHTML, およびキャプチャを取得。

What makes Geb groovy?

● Cross Browser(クロスブラウザ)● jQuery-like API(jQuery的なAPI)● Page Objects(ページオブジェクト)● Asynchronous Pages(非同期ページの対応)● Testing(テスティング)● Build Integration(ビルドツールとの統合)● Screen Scraping(スクリーンキャプチャ)

いますぐ体験

● https://github.com/geb/geb-example-gradle

● cloneして、gradlew コマンドで実行

いかがでした?

● 知らないことばかりで目から鱗だった● ためになることあった● 全部知ってた

いかがでした?

● 知らないことばかりで目から鱗だった● ためになることあった● 全部知ってた

今日の話

基本ここに書いてある

● GebのWebページ○ http://www.gebish.org/

● The Book Of Geb○ http://www.gebish.org/manual/current/

でも英語...な方に

The Book Of Gebの翻訳プロジェクト coming soon!?

Gebで長寿と繁栄を!

Special Thanks (敬称略)

@cocoatomo, @ffggss, @grimrose, @ito_nozomi, @kyon_mm, @nkns165, @nobusue, @oota_ken, @setoazusa, @syobochim, たけしふ, 嫁

おしまい