Successfully reported this slideshow.
We use your LinkedIn profile and activity data to personalize ads and to show you more relevant ads. You can change your ad preferences anytime.

What makes Geb groovy?

7,910 views

Published on

http://geb.connpass.com/event/11030/ での発表時資料です。

Published in: Software
  • If you want to download or read this book, copy link or url below in the New tab ......................................................................................................................... DOWNLOAD FULL PDF EBOOK here { http://bit.ly/2m77EgH } ......................................................................................................................... Download EPUB Ebook here { http://bit.ly/2m77EgH } .........................................................................................................................
       Reply 
    Are you sure you want to  Yes  No
    Your message goes here
  • DOWNLOAD THAT BOOKS/FILE INTO AVAILABLE FORMAT - (Unlimited) ......................................................................................................................... ......................................................................................................................... Download FULL PDF EBOOK here { http://bit.ly/2m77EgH } ......................................................................................................................... .............. Browse by Genre Available eBooks ......................................................................................................................... accessibility Books Library allowing access to top content, including thousands of title from favorite author, plus the ability to read or download a huge selection of books for your pc or smartphone within minutes Christian, Classics, Comics, Contemporary, Cookbooks, Art, Biography, Business, Chick Lit, Children's, Manga, Memoir, Music, Science, Science Fiction, Self Help, History, Horror, Humor And Comedy, Suspense, Spirituality, Sports, Thriller, Travel, Young Adult, Crime, Ebooks, Fantasy, Fiction, Graphic Novels, Historical Fiction, Mystery, Non Fiction, Paranormal, Philosophy, Poetry, Psychology, Religion, Romance,
       Reply 
    Are you sure you want to  Yes  No
    Your message goes here
  • DOWNLOAD THAT BOOKS/FILE INTO AVAILABLE FORMAT - (Unlimited) ......................................................................................................................... ......................................................................................................................... Download FULL PDF EBOOK here { http://bit.ly/2m6jJ5M } ......................................................................................................................... .............. Browse by Genre Available eBooks ......................................................................................................................... accessibility Books Library allowing access to top content, including thousands of title from favorite author, plus the ability to read or download a huge selection of books for your pc or smartphone within minutes Christian, Classics, Comics, Contemporary, Cookbooks, Art, Biography, Business, Chick Lit, Children's, Manga, Memoir, Music, Science, Science Fiction, Self Help, History, Horror, Humor And Comedy, Suspense, Spirituality, Sports, Thriller, Travel, Young Adult, Crime, Ebooks, Fantasy, Fiction, Graphic Novels, Historical Fiction, Mystery, Non Fiction, Paranormal, Philosophy, Poetry, Psychology, Religion, Romance,
       Reply 
    Are you sure you want to  Yes  No
    Your message goes here
  • accessibility Books Library allowing access to top content, including thousands of title from favorite author, plus the ability to read or download a huge selection of books for your pc or smartphone within minutes.........ACCESS WEBSITE Over for All Ebooks ..... (Unlimited) ......................................................................................................................... Download FULL PDF EBOOK here { http://bit.ly/2m6jJ5M } .........................................................................................................................
       Reply 
    Are you sure you want to  Yes  No
    Your message goes here
  • DOWNLOAD THAT BOOKS/FILE INTO AVAILABLE FORMAT - (Unlimited) ......................................................................................................................... ......................................................................................................................... Download FULL PDF EBOOK here { http://bit.ly/2m6jJ5M } ......................................................................................................................... .............. Browse by Genre Available eBooks ......................................................................................................................... accessibility Books Library allowing access to top content, including thousands of title from favorite author, plus the ability to read or download a huge selection of books for your pc or smartphone within minutes Christian, Classics, Comics, Contemporary, Cookbooks, Art, Biography, Business, Chick Lit, Children's, Manga, Memoir, Music, Science, Science Fiction, Self Help, History, Horror, Humor And Comedy, Suspense, Spirituality, Sports, Thriller, Travel, Young Adult, Crime, Ebooks, Fantasy, Fiction, Graphic Novels, Historical Fiction, Mystery, Non Fiction, Paranormal, Philosophy, Poetry, Psychology, Religion, Romance,
       Reply 
    Are you sure you want to  Yes  No
    Your message goes here

What makes Geb groovy?

  1. 1. What makes Geb groovy? @PoohSunny
  2. 2. こんにちは!
  3. 3. 自己紹介の前に いくつか質問
  4. 4. 質問1 Gebの利用経験 1. 知らなかった 2. 知ってたけど「げぶ」って読むと思ってた 3. インストールして、試しに動かしてみたことがある 4. 仕事や趣味のプロジェクトで使っている 5. むしろ作っている
  5. 5. 質問2 普段の利用言語
  6. 6. 質問3 WEBのGUIテスト自動化 ● してますか? ● どんな方法でしてますか? ○ Selenium IDE ? ○ WebDriver ?
  7. 7. 自己紹介 ● @PoohSunny ● 現在はカエルのマークのSaasスタートアップ チームで奮闘中 ● Groovy, Grails, Gradle, Geb を使ってお仕事 ● さっきの質問への回答 ○ Gebは仕事で使っています ○ 今はGroovy, 前はJava ○ Gebの前はWebDriver - Javaを使ってました
  8. 8. 質問4 WebDriverって、好きですか? ● 書くと長い(長過ぎる) ● driver.xxx とか書きたくない ● みんな同じようなラッパーを作っているらしい
  9. 9. そこで
  10. 10. Geb (「じぇぶ」と発音します) めっちゃgroovyなブラウザ操作自動化... WEB(の GUI)テスト、スクリーンキャプチャ、and more ※ groovyには「すばらしい」とか「イケてる」といった意味があります。 http://www.thefreedictionary.com/groovy
  11. 11. ● http://www.gebish.org/ ● Selenium WebDriverのラッパー ● Groovy製 ● 2015年1月時点でのバージョンは0.10.0 ● ライセンスはApache License Version 2.0
  12. 12. 今日触れること ● Gebひとまわり(これが大半) ○ Gebの良いところ洗いざらい ● はじめてみよう! ○ とりあえずさくっと始めてみるには 質問はいつでもどうぞ!! もしくは #gebjpつけて @PoohSunny にメンションく ださい!
  13. 13. 今日触れないこと ● 効果的なWEB GUIテストの書き方 ○ テスト戦略 ○ テスト計画 ○ 良いテストケースの作成方法 etc. ● Gebのハマりどころ ○ 次回以降の発表に期待!
  14. 14. 今日の話で ● 「おっ、Gebって良さそうじゃん。ちょっと手元で 動かしてみようかな」 ● 「試しに動かしてみた!」 となれば幸いです!!
  15. 15. でははじめましょう!
  16. 16. Geb’s Highlights(Gebのページより) ● Cross Browser ● jQuery-like API ● Page Objects ● Asynchronous Pages ● Testing ● Build Integration
  17. 17. Geb’s Highlights(Gebのページより) ● Cross Browser(クロスブラウザ) ● jQuery-like API(jQuery的なAPI) ● Page Objects(ページオブジェクト) ● Asynchronous Pages(非同期ページの対応) ● Testing(テスティング) ● Build Integration(ビルドツールとの統合)
  18. 18. 最初にこの2つから ● Cross Browser(クロスブラウザ) ● jQuery-like API(jQuery的なAPI) ● Page Objects(ページオブジェクト) ● Asynchronous Pages(非同期ページの対応) ● Testing(テスティング) ● Build Integration(ビルドツールとの統合)
  19. 19. 要するに 楽だ!
  20. 20. 百聞は一見にしかずということで サンプルコードを見 ながら体感してみま しょう。
  21. 21. サンプルコード(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(); }
  22. 22. 小さすぎて読めないので ● 基本省略します ○ インポート ○ クラス ○ コメント ● 今日のコードの半分くらいはhttps://github. com/PoohSunny/geb-study に上げてありま す。
  23. 23. サンプルコード(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(); }
  24. 24. 下準備 import geb.junit4.GebReportingTest; @RunWith(JUnit4) public class sampleTest extends GebReportingTest { これで、JUnit4でGebが利用できます。(最初はJUnit4で実行 します。)
  25. 25. まずはDriverの記述を減らし ましょう。
  26. 26. 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(); }
  27. 27. 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)内 で
  28. 28. 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(); }
  29. 29. 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が呼びます
  30. 30. 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() { }
  31. 31. 変数定義 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() { } 変数定義も不要
  32. 32. 変数定義 @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() { }
  33. 33. @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() { } 初期処理が不要に 終了処理が不要に
  34. 34. @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!")); }
  35. 35. 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!")); } スペースができたので コードを広げましょう
  36. 36. 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!")); }
  37. 37. Geb APIで簡潔に
  38. 38. 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!")); }
  39. 39. 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 メソッドで
  40. 40. 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 メソッドで
  41. 41. 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で書き換 えましょう
  42. 42. $() @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で書き換 えましょう
  43. 43. jQuery的なAPI ● 正式にはNavigator APIって名前 ● ふじさわさんの発表で詳しく! ● チートシート(日本語) ○ http://qiita.com/itagakishintaro/items/1fa06904bd0a6de73ee2 参考:http://www.gebish.org/content
  44. 44. 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も書き換え
  45. 45. 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も書き換え
  46. 46. << @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!")); } さらにこんな風にも書けま す
  47. 47. 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の記述は不要
  48. 48. 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の記述は不要
  49. 49. 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のキーストロー クもいけます
  50. 50. 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のキーストロー クもいけます
  51. 51. 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は冗長になってしまい がち...
  52. 52. 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ならスッキリ
  53. 53. 非同期ページへの対応 ● シンプルに書けるwait処理 参考:http://www.gebish.org/async
  54. 54. 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回目)
  55. 55. 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回目)
  56. 56. 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!")); } 余白を詰めてみます。
  57. 57. 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!")); }
  58. 58. これが 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(); }
  59. 59. こう @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!")); }
  60. 60. だいぶすっきりしました
  61. 61. でももう少し
  62. 62. groovyなコードに...
  63. 63. ● JVM向けの動的型付き言語 ● ほとんどゼロの学習曲線で現代的なプログラミン グ機能をJava開発者が利用できるようになる ● 構文がコンパクトなので、読みやすくメンテナン スしやすいコードになる ● http://groovy-lang.org/ ● Groovyを一回りしたい人はこちら ○ https://speakerdeck.com/glaforge/what-makes-groovy-groovy
  64. 64. 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 の記述不要
  65. 65. 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!")); }
  66. 66. セミコロン @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!")); } セミコロン不要
  67. 67. セミコロン @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!")) }
  68. 68. () @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!")) } パラメータがあれば () 不要
  69. 69. () @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!")) }
  70. 70. 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に書き換えてしまい ましょう!
  71. 71. 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!") } 短くなっただけでなく、別な メリットも
  72. 72. パワーアサートが使える!
  73. 73. というわけで、これが 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(); }
  74. 74. こう @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!") }
  75. 75. これが 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(); }
  76. 76. こう @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!") }
  77. 77. ここまでいかがです? けっこう簡潔になったでしょ?
  78. 78. 忘れてました 画面キャプチャの取得の話 GebReportingSpec, GebReportingTest を継承
  79. 79. スクリーンキャプチャの取得 @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
  80. 80. スクリーンキャプチャの取得 @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
  81. 81. ここまでの話 ● Cross Browser(クロスブラウザ) ● jQuery-like API(jQuery的なAPI) ● Page Objects(ページオブジェクト) ● Asynchronous Pages(非同期ページの対応) ● Testing(テスティング) ● Build Integration(ビルドツールとの統合) ● Screen Scraping(スクリーンキャプチャ)
  82. 82. 次の話 ● Cross Browser(クロスブラウザ) ● jQuery-like API(jQuery的なAPI) ● Page Objects(ページオブジェクト) ● Asynchronous Pages(非同期ページの対応) ● Testing(テスティング) ● Build Integration(ビルドツールとの統合) ● Screen Scraping(スクリーンキャプチャ)
  83. 83. ところで、このコード @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で動かすいみ がほとんどない
  84. 84. Gebのページを読むと GebはSpock, JUnit, TestNG, さらには (Cuke4Dukeを用いての)Cucumber といった、有 名なテスティングフレームワークへの統合モジュー ルを提供しています。 参考:http://www.gebish.org/testing
  85. 85. Gebのページを読むと GebはSpock, JUnit, TestNG, さらには (Cuke4Dukeを用いての)Cucumber といった、有 名なテスティングフレームワークへの統合モジュー ルを提供しています。 参考:http://www.gebish.org/testing
  86. 86. I’m Spock http://en.wikipedia.org/wiki/File:Leonard_Nimoy_as_Spock_1967.jpg お待たせしました!
  87. 87. 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 (句読点とか入れられる)
  88. 88. 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
  89. 89. 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
  90. 90. 他にもあるSpockの 便利機能
  91. 91. 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 をベースにしています
  92. 92. 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 をベースにしています テーブル記法でパラメー ターを定義できる
  93. 93. 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' でログインしようとすると '該 当するユーザーが見つかりませんでし た。' と表示する」 と補完してくれます。
  94. 94. ここまでの話 ● Cross Browser(クロスブラウザ) ● jQuery-like API(jQuery的なAPI) ● Page Objects(ページオブジェクト) ● Asynchronous Pages(非同期ページの対応) ● Testing(テスティング) ● Build Integration(ビルドツールとの統合) ● Screen Scraping(スクリーンキャプチャ)
  95. 95. 次の話 ● Cross Browser(クロスブラウザ) ● jQuery-like API(jQuery的なAPI) ● Page Objects(ページオブジェクト) ● Asynchronous Pages(非同期ページの対応) ● Testing(テスティング) ● Build Integration(ビルドツールとの統合) ● Screen Scraping(スクリーンキャプチャ)
  96. 96. クロスブラウザの自動化 ● サポート ○ Firefox ○ Internet Explorer ○ Google Chrome ○ Opera ○ Remote Browsers ○ Headless Browsers ■ HTMLUnit ■ PhantomJS ● 実験的にサポート ○ Chrome on Android ○ Safari on iPhone & iPad
  97. 97. 設定も簡単 environments { chrome { driver = { new ChromeDriver() } } firefox { driver = { new FirefoxDriver() } } phantomJs { driver = { new PhantomJSDriver() } } } ※システムプロパティに ”geb.env”という名前でパラメータをセットする
  98. 98. たとえば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 { // 省略 }
  99. 99. 他にもこんなツールと Apache Software Foundation
  100. 100. 最後の話 ● Cross Browser(クロスブラウザ) ● jQuery-like API(jQuery的なAPI) ● Page Objects(ページオブジェクト) ● Asynchronous Pages(非同期ページの対応) ● Testing(テスティング) ● Build Integration(ビルドツールとの統合) ● Screen Scraping(スクリーンキャプチャ)
  101. 101. ページオブジェクト ● Selenium界隈で広く利用されているデザインパ ターン ● テスト対象のページをオブジェクトとしてテストか ら切り離し、メンテナンス性を高める ● 参考URL ○ http://codezine.jp/article/detail/7527 ○ http://garbagetown.hatenablog. com/entry/2013/11/07/075011
  102. 102. ページオブジェクトパターンの要約 ● public メソッドは、そのページが提供するサービ スを表現します ● ページの内部を露出しないようにします ● 一般的に、アサーションは行いません ● メソッドは別の PageObjects を返します ● ページのすべてを表現する必要はありません ● 同じアクションの異なる結果は、異なるメソッドと してモデル化されます ※http://garbagetown.hatenablog.com/entry/2013/11/07/075011から引用
  103. 103. ページオブジェクト を標準サポート 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() } } }
  104. 104. 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クラスを継承
  105. 105. 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に遷移
  106. 106. 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 と記述するとチェックが走 る
  107. 107. ページ遷移にまつわるあれこれ 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() } } } このあたりは後でもう少し 詳しく触れます
  108. 108. 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 ブロック内に
  109. 109. 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してくれま す。
  110. 110. 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とし てまとめられる
  111. 111. 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() } } } 処理も書けます
  112. 112. 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() } } } 処理を呼び出した後の遷 移先を指定
  113. 113. 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() } } } ページ遷移が完了するま で待ってくれる
  114. 114. Pageを利用するテストケース class WhenCheckNotifyOnTheDashborad extends GebSpec { def "ログインしてメインページで3件の新着通知を確認する"() { when: "サインインページを表示して" to SignInPage at SignInPage and: "ユーザー名とパスワードを入力してログインすると" signIn "SAMPLE_USER", "SAMPLE_PASSWORD" then: "メインページを表示する" at MainPage when: "ページ内のダッシュボードを確認すると" // ...略 }
  115. 115. to class WhenCheckNotifyOnTheDashborad extends GebSpec { def "ログインしてメインページで3件の新着通知を確認する"() { when: "サインインページを表示して" to SignInPage at SignInPage and: "ユーザー名とパスワードを入力してログインすると" signIn "SAMPLE_USER", "SAMPLE_PASSWORD" then: "メインページを表示する" at MainPage when: "ページ内のダッシュボードを確認すると" // ...略 } SingInPageへ遷移 & ページオブジェクトを 移動
  116. 116. at class WhenCheckNotifyOnTheDashborad extends GebSpec { def "ログインしてメインページで3件の新着通知を確認する"() { when: "サインインページを表示して" to SignInPage at SignInPage and: "ユーザー名とパスワードを入力してログインすると" signIn "SAMPLE_USER", "SAMPLE_PASSWORD" then: "メインページを表示する" at MainPage when: "ページ内のダッシュボードを確認すると" // ...略 } to でページ遷移すると at チェックがされるので、この 例では不要
  117. 117. contentの呼び出し class WhenCheckNotifyOnTheDashborad extends GebSpec { def "ログインしてメインページで3件の新着通知を確認する"() { when: "サインインページを表示して" to SignInPage at SignInPage and: "ユーザー名とパスワードを入力してログインすると" signIn "SAMPLE_USER", "SAMPLE_PASSWORD" then: "メインページを表示する" at MainPage when: "ページ内のダッシュボードを確認すると" // ...略 } SingInPage 内の処理の 呼び出し
  118. 118. ページ遷移 class WhenCheckNotifyOnTheDashborad extends GebSpec { def "ログインしてメインページで3件の新着通知を確認する"() { when: "サインインページを表示して" to SignInPage at SignInPage and: "ユーザー名とパスワードを入力してログインすると" signIn "SAMPLE_USER", "SAMPLE_PASSWORD" then: "メインページを表示する" at MainPage when: "ページ内のダッシュボードを確認すると" // ...略 } この処理内で MainPage への遷移 + ページオブ ジェクトの変更
  119. 119. at class WhenCheckNotifyOnTheDashborad extends GebSpec { def "ログインしてメインページで3件の新着通知を確認する"() { when: "サインインページを表示して" to SignInPage at SignInPage and: "ユーザー名とパスワードを入力してログインすると" signIn "SAMPLE_USER", "SAMPLE_PASSWORD" then: "メインページを表示する" at MainPage when: "ページ内のダッシュボードを確認すると" // ...略 } 次のページの at チェック を実行
  120. 120. 別な書き方 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
  121. 121. ページを明示的に記述 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
  122. 122. 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
  123. 123. 補足:ページ遷移にまつわるAPI リクエストを作るGebのAPI群 ● to() ● via() ● go()
  124. 124. 補足:ページ遷移にまつわるAPI ● to() ● via() -----------------まずはここの違い ● go()
  125. 125. 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をベースにしています
  126. 126. 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がセットされ ていて
  127. 127. 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メソッドを呼んでも
  128. 128. 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のまま
  129. 129. 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は変わる
  130. 130. 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をベースにしています
  131. 131. 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がセットされ ている
  132. 132. 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メソッドを呼び出すと
  133. 133. 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が セットされる)
  134. 134. 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も変わる
  135. 135. to()とvia()の違い ● メソッド呼び出し後に atチェックが走るか ○ to() => 走る ○ via() => 走らない ■ 遷移先ページでリダイレクトがあるような場合はvia() を使う ● to() か、at チェックを組み合わせた via() をい つも使うのが良いアイデア 参考:http://www.gebish.org/manual/current/all.html#at_checking
  136. 136. to(),via(),go()まとめ メソッド 呼び出し後にページオブ ジェクトをセットするか 呼び出し後にatチェックを するか to() する する via() する しない go() しない しない
  137. 137. いかがでした? 興味出てきました?
  138. 138. でも始めるのは難しそう?
  139. 139. とりあえずさくっと体験してみたい方 ● オススメ体験法 ○ 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
  140. 140. まとめ
  141. 141. What makes Geb groovy?
  142. 142. クロスブラウザの自動化 ● サポート ○ Firefox ○ Internet Explorer ○ Google Chrome ○ Opera ○ Remote Browsers ○ Headless Browsers ■ HTMLUnit ■ PhantomJS ● 実験的にサポート ○ Chrome on Android ○ Safari on iPhone & iPad
  143. 143. jQuery的なAPI 参考:http://www.gebish.org/content
  144. 144. ページオブジェクトのサポート 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
  145. 145. 非同期ページへの対応 ● シンプルに書けるwait処理 参考:http://www.gebish.org/async
  146. 146. テスティング GebはSpock, JUnit, TestNG, さらには (Cuke4Dukeを用いての)Cucumber といった、有 名なテスティングフレームワークへの統合モジュー ルを提供しています。 参考:http://www.gebish.org/testing
  147. 147. 他ツールとの統合 Apache Software Foundation
  148. 148. スクリーンキャプチャ @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, およびキャプ チャを取得。
  149. 149. What makes Geb groovy? ● Cross Browser(クロスブラウザ) ● jQuery-like API(jQuery的なAPI) ● Page Objects(ページオブジェクト) ● Asynchronous Pages(非同期ページの対応) ● Testing(テスティング) ● Build Integration(ビルドツールとの統合) ● Screen Scraping(スクリーンキャプチャ)
  150. 150. いますぐ体験 ● https://github.com/geb/geb-example- gradle ● cloneして、gradlew コマンドで実行
  151. 151. いかがでした? ● 知らないことばかりで目から鱗だった ● ためになることあった ● 全部知ってた
  152. 152. いかがでした? ● 知らないことばかりで目から鱗だった ● ためになることあった ● 全部知ってた
  153. 153. 今日の話 基本ここに書いてある ● GebのWebページ ○ http://www.gebish.org/ ● The Book Of Geb ○ http://www.gebish.org/manual/current/
  154. 154. でも英語...な方に The Book Of Gebの翻訳プロジェクト coming soon!?
  155. 155. Gebで長寿と繁栄を!
  156. 156. Special Thanks (敬称略) @cocoatomo, @ffggss, @grimrose, @ito_nozomi, @kyon_mm, @nkns165, @nobusue, @oota_ken, @setoazusa, @syobochim, たけしふ, 嫁
  157. 157. おしまい

×