SlideShare una empresa de Scribd logo
1 de 57
Descargar para leer sin conexión
Testing Web Applications
with GEB
Howard M. Lewis Ship
TWD Consulting
hlship@gmail.com
@hlship
                       © 2012 Howard M. Lewis Ship
Testing Web Applications




                    What's your process?
                      Manual?
                      Selenium?
                    What about Ajax?
Spoiled by jQuery

                <div class="summary">

                     …

                     <div class="subtotal">107.95</div>




           $(".order-summary .subtotal").text() == "107.95"




http://jquery.com/
Selenium WebDriver

   Drives:




   or limited HtmlUnit (browserless)



http://seleniumhq.org/
WebDriver driver = new FirefoxDriver();

driver.get("http://localhost:8080/order-summary/12345");

WebElement element = driver.findElement(
  By.cssSelector(".order-summary .subtotal");

assertEquals(element.getText(), "107.95");
driver.get("http://www.google.com");

WebElement element = driver.findElement(By.name("q"));

element.sendKeys("Cheese!");

element.submit();    Triggers Ajax Update

new WebDriverWait(driver, 10)).until(new ExpectedCondition<Boolean>() {
    public Boolean apply(WebDriver d) {
        return d.getTitle().toLowerCase().startsWith("cheese!");
    }
};
Java == High Ceremony
Groovy == Low Ceremony
       + Dynamic
Power of Selenium WebDriver 2.15.0
Elegance of jQuery content selection
Robustness of Page Object modelling
Expressiveness of Groovy
First Class Documentation
Running GEB Interactively

geb.groovy
import groovy.grape.Grape
Grape.grab([group:'org.codehaus.geb', module:'geb-core', version:'0.6.3'])
Grape.grab([group:'org.seleniumhq.selenium', module:'selenium-firefox-driver',
  version:'2.15.0'])
Grape.grab([group:'org.seleniumhq.selenium', module:'selenium-support', ⏎
version:'2.15.0'])
import geb.*

import java.util.logging.*

new File("geb-logging.properties").withInputStream { ⏎
  LogManager.logManager.readConfiguration it }


geb-logging.properties
handlers=java.util.logging.ConsoleHandler

java.util.logging.ConsoleHandler.level=WARNING
java.util.logging.ConsoleHandler.formatter=java.util.logging.SimpleFormatter
GEB Interactive
   $ groovysh
   Groovy Shell (1.8.6, JVM: 1.6.0_29)
   Type 'help' or 'h' for help.
   -------------------------------------------------
   groovy:000> . geb.groovy
   ===> [import groovy.grape.Grape]
   ===> null
   ===> null
   ===> null
   ===> [import groovy.grape.Grape, import geb.*]
   ===> [import groovy.grape.Grape, import geb.*, import ⏎
   java.util.logging.*]
   ===> null
   groovy:000> b = new Browser(baseUrl: "http://google.com/")
   ===> geb.Browser@190a0d51
   groovy:000> b.go()
   ===> null
   groovy:000>
Google Demo
Google Demo

 b = new Browser(baseUrl: "http://google.com/")
 b.go()
 b.$("input", name:"q") << "geb"
 b.waitFor { b.title.toLowerCase().startsWith("geb") }
 b.$("a.l").text()
 b.$("a.l")*.text()
 b.$("a.l")*.@href
 b.$("input", name:"q") << "geb groovy" 
 b.$("a.l").first().click()
 b.quit()
             Not needed in 0.7
IMDB Demo
IMDB Demo

  b = new Browser()
  b.baseUrl = "http://imdb.com"
  b.go()
  b.$("#navbar-query") << "Bladerunner"
  b.$("#navbar-submit-button").click()
  b.waitFor { b.title == "IMDb Search" }
  b.$("#main table", 1).find("a")*.text()
  b.$("#main table", 1).find("a", text:"Blade Runner").click()
  assert b.$(".star-box-giga-star").text() == "9.9"
  b.$("table.cast_list td.name a")[0..3]*.text()
Browser                                 Page                         Navigator
 go() : void      Delegation         pageUrl : String                      x : int
quit() : void                           title : String                     y : int
page : Page                          $(…) : Navigator                   width : int
                                    waitFor(…) : Object                 height : int
                               startsWith(…) : TextMatcher         disabled : boolean
                                contains(…) : TextMatcher            empty : boolean
     Delegation




                               endsWith(…) : TextMatcher          displayed : boolean
                                                                add(String) : Navigator
                                                                       click() : void
                                                                  filter(...) : Navigator
 GebSpec                                                          find(…) : Navigator
                                                                not(String) : Navigator
                                                                    first() : Navigator
                                                                    last() : Navigator
                                                                 getAt(…) : Navigator
                                                              getAttribute(String) : String
                                                                has(String) : Navigator
                                                              parents(String) : Navigator
                                                             parentsUntil(String): Navigator
                                                                        size() : int
                                                                      text() : String
                                                                     value() : Object
$(css selector, index, attribute / text matchers)




 $("p") ➠ all <p>
                              CS
 $("p", 3) ➠ 4th <p>            S3

 $("p")[3] ➠ 4th <p>
 $("p")[0..2] ➠ 1st through 3rd <p>
Attribute Matchers
 $("a", text:"Blade Runner")

 ➠ All <a> tags whose text is "Blade Runner"
 $("a", href: contains("/name/")

 ➠ All <a> tags whose href attribute contains "/name/"
 $("a", href: ~/nm[0-9]+/)

 ➠ All <a> tags whose href attribute matches the
 pattern
Attribute Predicates
      Case Sensitive           Case Insensitive                       Description


startsWith             iStartsWith                start with value


contains               iContains                  contains the value anywhere


endsWith               iEndsWith                  end with value

                                                  contains value surrounded by whitespace (or at
constainsWord          iContainsWord
                                                  begin or end)

notStartsWith          iNotStartsWith             DOES NOT start with value


notContains            iNotContains               DOES NOT contain value anywhere


notEndsWith            iNotEndsWith               DOES NOT end with value

                                                  DOES NOT contain value (surrounded by
notContainsWord        iNotContainsWord
                                                  whitespace, or at begin or end)
Relative Traversal
                        <div class="a">
                            <div class="b">
                                <p class="c"></p>
                                <p class="d"></p>
                                <p class="e"></p>
                            </div>
                            <div class="f"></div>
                        </div>

$("p.d").previous()                 p.c

$("p.e").prevAll()                  p.c p.d

$("p.d").next()                     p.e

$("p.c").nextAll()                  p.d p.e

$("p.cd").parent()                  div.b

$("p.c").siblings()                 p.d p.e

$("div.a").children()               div.b div.f
Navigators are Groovy Collections

                                               each() is a Groovy Collection method
groovy:000> castList = [:]
===> {}
groovy:000> b.$("table.cast_list tr").tail().each
{ castList[it.find("td.name").text()] = it.find("td.character").text() }
===> […]
groovy:000> castList
===> {Harrison Ford=Rick Deckard, Rutger Hauer=Roy Batty, Sean Young=Rachael,
Edward James Olmos=Gaff, M. Emmet Walsh=Bryant, Daryl Hannah=Pris, William
Sanderson=J.F. Sebastian, Brion James=Leon Kowalski, Joe Turkel=Dr. Eldon
Tyrell, Joanna Cassidy=Zhora, James Hong=Hannibal Chew, Morgan Paull=Holden,
Kevin Thompson=Bear, John Edward Allen=Kaiser, Hy Pyke=Taffey Lewis}




http://groovy.codehaus.org/groovy-jdk/java/util/Collection.html
Forms

           <form id="navbar-form" …
             <input type="text" name="q" …




  groovy:000> b.q = "Galaxy Quest"
  ===> Galaxy Quest




  groovy:000> b.$("#navbar-form").q
  ===> Galaxy Quest
Pages and Modules
Problem: Repetition

$("a", text:"Contact Us").click()

waitFor { b.title == "Contact Us" }
                            $(".alert .btn-primary").click()

                            waitFor { b.title == "Contact Us" }




     ($(".search-form input[name='query']") << "search term").submit()
Solution: Model Pages not just DOM
    Browser                                Page
    go() : void    Delegation         pageUrl : String
   quit() : void                         title : String
   page : Page                       $(…) : Navigator
 at(…) : boolean                    waitFor(…) : Object
                                    verifyAt() : boolean
                                     browser: Browser

           class Home extends geb.Page {
             static url = ""
             static at = {
               title == "The Internet Movie Database (IMDb)"
             }
           }

           groovy:000>     b.$(".home").click(Home)
           ===> null
           groovy:000>     b.verifyAt()
           ===> true
           groovy:000>     b.at Home
           ===> true
           groovy:000>     b.page
           ===> Home
           groovy:000>     b.to Home
           ===> null
Page Content
 class Home extends Page {
   static at = { title == "The Internet Movie Database (IMDb)" }
   static url = ""
   static content = {
     boxOffice { $("h3", text:"Box Office").parent() }
     firstBoxOffice { boxOffice.find("a").first() }
   }
 }




 groovy:000> b.firstBoxOffice.click()
 ===> null
to / do / at

         groovy:000> b.to Home
         ===> null
         groovy:000> b.q = "Forbidden Planet"
         ===> Forbidden Planet
         groovy:000> b.searchForm.go.click()
         ===> null
         groovy:000> b.at Search
         ===> true
         groovy:000>
Content Options




static content = {
  boxOffice { $("h3", text:"Box Office").parent() }
  boxOfficeLinks { boxOffice.find("a", text: iNotStartsWith("see more")) }
  movieShowtimes(required:false) { $("h3", text:"Movie Showtimes").parent() }
  movieShowtimesGo(required:false) { movieShowtimes.find("input", value:"Go") }
}
Content Options
           Option               Type                Default           Description


                                                              Evaluate content once, or
cache               boolean                 false
                                                              on each access


                                                              Error on page load if
                                                              content does not exist (use
required            boolean                 true
                                                              false for optional or Ajax-
                                                              loaded)


                    Page or Class,                            On a link, identify the page
to                                          null
                    list of Page or Class                     the link submits to



                                                              Wait for content to become
wait                varies                  null
                                                              available (via Ajax/DHTML)
Page Methods
class Home extends Page {
  static at = { title == "The Internet Movie Database (IMDb)" }
  static url = ""
  static content = {
    boxOffice { $("h3", text:"Box Office").parent() }
    boxOfficeLinks { boxOffice.find("a", text: iNotStartsWith("see more")) }
  }

    def clickBoxOffice(index) {
      def link = boxOfficeLinks[index]
      def label = link.text()
      link.click()
      waitFor { title.startsWith(label) }
    }
}


groovy:000> b.to Home
===> null
groovy:000> b.clickBoxOffice 0
===> true
groovy:000>
Problem: Re-used web pages
          class Movie extends Page {

              static at = {
                assert title.startsWith("Blade Runner")
                true                                      GEB 0.7 will magically
              }                                            convert to asserts
              static content = {
                rating { $(".star-box-gig-start").text() }
                castList { $("table.cast_list tr").tail() }
              }
          }


 groovy:000> b.$("#main table", 2).find("a",7).click(Movie)
 ===> null
 groovy:000> b.page
 ===> Movie
 groovy:000> b.verifyAt()
 ERROR org.codehaus.groovy.runtime.powerassert.PowerAssertionError:
 assert title.startsWith("Blade Runner")
        |     |
        |     false
        The Bugs Bunny/Road-Runner Movie (1979) - IMDb
Solution: Configured Page Instances

     class Movie extends Page {
       String expectedTitle

         static at = {
           assert title.startsWith expectedTitle
           true
         }

         static content = {
           rating { $(".star-box-gig-start").text() }
           castList { $("table.cast_list tr").tail() }
         }
     }
class Search extends Page {
  static at = {
    assert title == "IMDb Search"
    true
  }

    static content = {
      mainTable { $("#main table") }
      matchingTitles { mainTable[2] }
      matchingTitlesLinks { matchingTitles.find("a", ⏎
                    href: contains("/title/tt")) }
    }

    def clickMatchingTitle(int index) {
      def link = matchingTitlesLinks[index]
      def label = link.text()
      link.click()
      browser.at new Movie(expectedTitle: label)
    }
}
click()




   groovy:000> b.searchForm.go.click()
   ===> null
   groovy:000> b.clickMatchingTitle 3
   ===> true




def clickMatchingTitle(int index) {
  def link = matchingTitlesLinks[index]
  def label = link.text()
  link.click()
  browser.at new Movie(expectedTitle: label)
}
Problem: Duplication on Pages
               b.$("#navbar-query") << "Bladerunner"
               b.$("#navbar-submit-button").click()




 Other examples:
   Login / Logout / Register
   "Contact Us" & other boilerplate
   "Mark Favorite"
   View Product Details
   Bid / Buy
Solution: Modules
  class SearchForm extends geb.Module {
    static content = {
      field { $("#navbar-query") }
      go(to: Search) { $("#navbar-submit-button") }
    }
  }

  class Home extends Page {
    static at = { title == "The Internet Movie Database (IMDb)" }
    static url = ""
    static content = {
      searchForm { module SearchForm }
    }
  }



  groovy:000> b.to Home
  ===> null
  groovy:000> b.searchForm.field << "Serenity"
  ===> [org.openqa.selenium.firefox.FirefoxWebElement@1ef44b1f]
  groovy:000> b.searchForm.go.click()
  ===> null
  groovy:000> b.page
  ===> Search
Problem: Repeating Elements




        <table class="cast_list">
          <tr>
            <td class="primary_photo"> …
            <td class="name"> …
            <td class="ellipsis"> …
            <td class="character"> …
Solution: Module Lists
class CastRow extends Module {
  static content = {
    actorName { $("td.name").text() }
    characterName { $("td.character").text() }
  }
}                    Scope limited to each <tr>
class Movie extends Page {
  String expectedTitle

    static at = { title.startsWith expectedTitle }

    static content = {
      rating { $(".star-box-gig-start").text() }
      castList { moduleList CastRow, $("table.cast_list tr").tail() }
    }
}
groovy:000> b.at(new Movie(expectedTitle: "Blade Runner"))
===> true
groovy:000> b.castList[0].actorName
===> Harrison Ford
groovy:000> b.castList[0].characterName
===> Rick Deckard
groovy:000> b.castList*.actorName
===> [Harrison Ford, Rutger Hauer, Sean Young, Edward James Olmos, ⏎
M. Emmet Walsh, Daryl Hannah, William Sanderson, Brion James, Joe Turkel, ⏎
Joanna Cassidy, James Hong, Morgan Paull, Kevin Thompson, John Edward Allen, ⏎
Hy Pyke]
JavaScript and Ajax
js object


   groovy:000> b = new Browser(baseUrl: "http://jquery.org")
   ===> geb.Browser@5ec22978
   groovy:000> b.go()
   ===> null
   groovy:000> b.js."document.title"
   ===> jQuery Project

                          Access simple page properties
Executing JavaScript
                                 Text evaluated in-browser

  groovy:000> b.js.exec '''
  groovy:001>   $("img").css("background-color", "red").fadeOut()
  groovy:002> '''
  ===> null
  groovy:000> b.js.exec 1, 2, "return arguments[0] + arguments[1];"
  ===> 3
jQuery Hook

                                      More methodMissing() magic!

  groovy:000> b.$("img").jquery.fadeIn()
  ===> [org.openqa.selenium.firefox.FirefoxWebElement@de86fd70,
  org.openqa.selenium.firefox.FirefoxWebElement@615e6612,
                             Silently fails unless jQuery on page
  org.openqa.selenium.firefox.FirefoxWebElement@52c13174,
  org.openqa.selenium.firefox.FirefoxWebElement@69e1ba19,
  org.openqa.selenium.firefox.FirefoxWebElement@2797f147,
  org.openqa.selenium.firefox.FirefoxWebElement@69cfbbda,
  org.openqa.selenium.firefox.FirefoxWebElement@27d5741a,
  org.openqa.selenium.firefox.FirefoxWebElement@10b36232,
  org.openqa.selenium.firefox.FirefoxWebElement@ec3e243f]
Waiting
          waitFor   { condition }
          waitFor   timeout { condition }
          waitFor   timeout, interval { condition }
          waitFor   "preset" { condition }
Testing Framework
Integration
Reporting
package myapp.tests
                                Base class that reports at end of each test
import geb.spock.*

class Login extends GebReportingSpec {

    def "successful login"() {

        when:
        go "login"
        username = "user1"
        report "login screen"             Capture HTML and screenshot
        login().click()

        then:
        title == "Welcome, User1"
    }
}

                            reports/myapp/tests/Login/1-1-login-login screen.html
                            reports/myapp/tests/Login/1-1-login-login screen.png
                            reports/myapp/tests/Login/1-2-login-end.html
                            reports/myapp/tests/Login/1-2-login-end.png
Base Classes
                                                 Report end of each test

Framework   Artifact     Base Class           Reporting Base Class

Spock       geb-spock    geb.spock.GebSpec    geb.spock.GebReportingSpec

Junit 4     geb-juni4    geb.junit4.GebTest   geb.junit4.GebReportingTest


Junit 3     geb-junit3   geb.junit3.GebTest   geb.junit3.GebReportingTest


TestNG      geb-testng   geb.testng.GebTest   geb.testng.GebReportingTest



                               Report failures only
Delegation
package myapp.tests                            Page


import geb.spock.*




                                                 Delegation
class Login extends GebReportingSpec {

    def "successful login"() {                Browser

        when:




                                                 Delegation
        go "login"
        username = "user1"
        report "login screen"
        login().click()
                                         Geb[Reporting]Spec

        then:
        title == "Welcome, User1"
    }
}
Configuration
GebConfig.groovy
       src/test/resources/GebConfig.groovy
       import org.openqa.selenium.firefox.FirefoxDriver
       import org.openqa.selenium.chrome.ChromeDriver

       driver = { new FirefoxDriver() } // use firefox by default

       waiting {
           timeout = 2 // default wait is two seconds
       }

       environments {
           chrome {
               driver = { new ChromeDriver() }
           }
       }




http://groovy.codehaus.org/gapi/groovy/util/ConfigSlurper.html
Environment
              $ grade test -Dgeb.env=chrome




   src/test/resources/GebConfig.groovy
   import org.openqa.selenium.firefox.FirefoxDriver
   import org.openqa.selenium.chrome.ChromeDriver

   driver = { new FirefoxDriver() } // use firefox by default

   waiting {
       timeout = 2 // default wait is two seconds
   }

   environments {
       chrome {
           driver = { new ChromeDriver() }
       }
   }
Waiting Configuration

      GebConfig.groovy
      waiting {
        timeout = 10
        retryInterval = 0.5

          presets {
            slow { timeout = 20, retryInterval = 1 }
            quick { timeout = 1 }
          }
      }
More Info
http://www.gebish.org
https://github.com/geb/geb
http://howardlewisship.com
Q&A

Más contenido relacionado

La actualidad más candente

Groovy and Grails in Action - Devoxx 2008 - University - Guillaume Laforge
Groovy and Grails in Action - Devoxx 2008 - University - Guillaume LaforgeGroovy and Grails in Action - Devoxx 2008 - University - Guillaume Laforge
Groovy and Grails in Action - Devoxx 2008 - University - Guillaume LaforgeGuillaume Laforge
 
Joe Walker Interactivewebsites Cometand Dwr
Joe Walker Interactivewebsites Cometand DwrJoe Walker Interactivewebsites Cometand Dwr
Joe Walker Interactivewebsites Cometand Dwrdeimos
 
Remy Sharp The DOM scripting toolkit jQuery
Remy Sharp The DOM scripting toolkit jQueryRemy Sharp The DOM scripting toolkit jQuery
Remy Sharp The DOM scripting toolkit jQuerydeimos
 
Learning jQuery made exciting in an interactive session by one of our team me...
Learning jQuery made exciting in an interactive session by one of our team me...Learning jQuery made exciting in an interactive session by one of our team me...
Learning jQuery made exciting in an interactive session by one of our team me...Thinqloud
 
History of jQuery
History of jQueryHistory of jQuery
History of jQueryjeresig
 
Introduction to Nodejs
Introduction to NodejsIntroduction to Nodejs
Introduction to NodejsGabriele Lana
 
A Short Introduction To jQuery
A Short Introduction To jQueryA Short Introduction To jQuery
A Short Introduction To jQuerySudar Muthu
 
jQuery%20on%20Rails%20Presentation
jQuery%20on%20Rails%20PresentationjQuery%20on%20Rails%20Presentation
jQuery%20on%20Rails%20Presentationguestcf600a
 
JavaScript & HTML5 - Brave New World
JavaScript & HTML5 - Brave New WorldJavaScript & HTML5 - Brave New World
JavaScript & HTML5 - Brave New WorldRobert Nyman
 
jQuery Loves Developers - Oredev 2009
jQuery Loves Developers - Oredev 2009jQuery Loves Developers - Oredev 2009
jQuery Loves Developers - Oredev 2009Remy Sharp
 
Building High Performance Web Applications and Sites
Building High Performance Web Applications and SitesBuilding High Performance Web Applications and Sites
Building High Performance Web Applications and Sitesgoodfriday
 

La actualidad más candente (18)

jQuery
jQueryjQuery
jQuery
 
jQuery in 15 minutes
jQuery in 15 minutesjQuery in 15 minutes
jQuery in 15 minutes
 
JQuery introduction
JQuery introductionJQuery introduction
JQuery introduction
 
Jquery-overview
Jquery-overviewJquery-overview
Jquery-overview
 
Groovy and Grails in Action - Devoxx 2008 - University - Guillaume Laforge
Groovy and Grails in Action - Devoxx 2008 - University - Guillaume LaforgeGroovy and Grails in Action - Devoxx 2008 - University - Guillaume Laforge
Groovy and Grails in Action - Devoxx 2008 - University - Guillaume Laforge
 
Joe Walker Interactivewebsites Cometand Dwr
Joe Walker Interactivewebsites Cometand DwrJoe Walker Interactivewebsites Cometand Dwr
Joe Walker Interactivewebsites Cometand Dwr
 
Remy Sharp The DOM scripting toolkit jQuery
Remy Sharp The DOM scripting toolkit jQueryRemy Sharp The DOM scripting toolkit jQuery
Remy Sharp The DOM scripting toolkit jQuery
 
Learning jQuery made exciting in an interactive session by one of our team me...
Learning jQuery made exciting in an interactive session by one of our team me...Learning jQuery made exciting in an interactive session by one of our team me...
Learning jQuery made exciting in an interactive session by one of our team me...
 
The jQuery Divide
The jQuery DivideThe jQuery Divide
The jQuery Divide
 
History of jQuery
History of jQueryHistory of jQuery
History of jQuery
 
groovy & grails - lecture 2
groovy & grails - lecture 2groovy & grails - lecture 2
groovy & grails - lecture 2
 
Introduction to Nodejs
Introduction to NodejsIntroduction to Nodejs
Introduction to Nodejs
 
A Short Introduction To jQuery
A Short Introduction To jQueryA Short Introduction To jQuery
A Short Introduction To jQuery
 
jQuery%20on%20Rails%20Presentation
jQuery%20on%20Rails%20PresentationjQuery%20on%20Rails%20Presentation
jQuery%20on%20Rails%20Presentation
 
JavaScript & HTML5 - Brave New World
JavaScript & HTML5 - Brave New WorldJavaScript & HTML5 - Brave New World
JavaScript & HTML5 - Brave New World
 
jQuery Loves Developers - Oredev 2009
jQuery Loves Developers - Oredev 2009jQuery Loves Developers - Oredev 2009
jQuery Loves Developers - Oredev 2009
 
Building High Performance Web Applications and Sites
Building High Performance Web Applications and SitesBuilding High Performance Web Applications and Sites
Building High Performance Web Applications and Sites
 
jQuery PPT
jQuery PPTjQuery PPT
jQuery PPT
 

Destacado

Jenkinsを用いたAndroidアプリビルド作業効率化
Jenkinsを用いたAndroidアプリビルド作業効率化Jenkinsを用いたAndroidアプリビルド作業効率化
Jenkinsを用いたAndroidアプリビルド作業効率化Kenichi Kambara
 
Gradle talk, Javarsovia 2010
Gradle talk, Javarsovia 2010Gradle talk, Javarsovia 2010
Gradle talk, Javarsovia 2010Tomek Kaczanowski
 
The outlineoftestprocess
The outlineoftestprocessThe outlineoftestprocess
The outlineoftestprocesskyon mm
 
Groovy Testing Aug2009
Groovy Testing Aug2009Groovy Testing Aug2009
Groovy Testing Aug2009guest4a266c
 
AgileJapan2010 基調講演:野中郁次郎先生による「実践知のリーダシップ~スクラムと知の場作り」
AgileJapan2010 基調講演:野中郁次郎先生による「実践知のリーダシップ~スクラムと知の場作り」AgileJapan2010 基調講演:野中郁次郎先生による「実践知のリーダシップ~スクラムと知の場作り」
AgileJapan2010 基調講演:野中郁次郎先生による「実践知のリーダシップ~スクラムと知の場作り」Kenji Hiranabe
 
うさぎ組 in G* WorkShop -うさみみの日常-
うさぎ組 in G* WorkShop -うさみみの日常-うさぎ組 in G* WorkShop -うさみみの日常-
うさぎ組 in G* WorkShop -うさみみの日常-kyon mm
 
Groovier testing with Spock
Groovier testing with SpockGroovier testing with Spock
Groovier testing with SpockRobert Fletcher
 
Spock Framework 2
Spock Framework 2Spock Framework 2
Spock Framework 2Ismael
 
G*におけるソフトウェアテスト・シーズンIII
G*におけるソフトウェアテスト・シーズンIIIG*におけるソフトウェアテスト・シーズンIII
G*におけるソフトウェアテスト・シーズンIIITakuma Watabiki
 
Groovy 1.8の新機能について
Groovy 1.8の新機能についてGroovy 1.8の新機能について
Groovy 1.8の新機能についてUehara Junji
 
Jenkinsプラグインの作り方
Jenkinsプラグインの作り方Jenkinsプラグインの作り方
Jenkinsプラグインの作り方Kiyotaka Oku
 
Androidリリース作業の効率化(2)
Androidリリース作業の効率化(2)Androidリリース作業の効率化(2)
Androidリリース作業の効率化(2)Kenichi Kambara
 
Spock Framework
Spock FrameworkSpock Framework
Spock FrameworkIsmael
 
Groovy, Transforming Language
Groovy, Transforming LanguageGroovy, Transforming Language
Groovy, Transforming LanguageUehara Junji
 
Gradle a new Generation Build Tool
Gradle a new Generation Build ToolGradle a new Generation Build Tool
Gradle a new Generation Build ToolShinya Mochida
 
function list
function listfunction list
function listkyon mm
 
レガシーコード改善はじめました 横浜道場
レガシーコード改善はじめました 横浜道場レガシーコード改善はじめました 横浜道場
レガシーコード改善はじめました 横浜道場Hiroyuki Ohnaka
 

Destacado (20)

Jenkinsを用いたAndroidアプリビルド作業効率化
Jenkinsを用いたAndroidアプリビルド作業効率化Jenkinsを用いたAndroidアプリビルド作業効率化
Jenkinsを用いたAndroidアプリビルド作業効率化
 
Spockを使おう!
Spockを使おう!Spockを使おう!
Spockを使おう!
 
Gradle talk, Javarsovia 2010
Gradle talk, Javarsovia 2010Gradle talk, Javarsovia 2010
Gradle talk, Javarsovia 2010
 
The outlineoftestprocess
The outlineoftestprocessThe outlineoftestprocess
The outlineoftestprocess
 
Groovy Testing Aug2009
Groovy Testing Aug2009Groovy Testing Aug2009
Groovy Testing Aug2009
 
AgileJapan2010 基調講演:野中郁次郎先生による「実践知のリーダシップ~スクラムと知の場作り」
AgileJapan2010 基調講演:野中郁次郎先生による「実践知のリーダシップ~スクラムと知の場作り」AgileJapan2010 基調講演:野中郁次郎先生による「実践知のリーダシップ~スクラムと知の場作り」
AgileJapan2010 基調講演:野中郁次郎先生による「実践知のリーダシップ~スクラムと知の場作り」
 
うさぎ組 in G* WorkShop -うさみみの日常-
うさぎ組 in G* WorkShop -うさみみの日常-うさぎ組 in G* WorkShop -うさみみの日常-
うさぎ組 in G* WorkShop -うさみみの日常-
 
Groovier testing with Spock
Groovier testing with SpockGroovier testing with Spock
Groovier testing with Spock
 
Spock Framework 2
Spock Framework 2Spock Framework 2
Spock Framework 2
 
G*におけるソフトウェアテスト・シーズンIII
G*におけるソフトウェアテスト・シーズンIIIG*におけるソフトウェアテスト・シーズンIII
G*におけるソフトウェアテスト・シーズンIII
 
Groovy 1.8の新機能について
Groovy 1.8の新機能についてGroovy 1.8の新機能について
Groovy 1.8の新機能について
 
Jenkinsプラグインの作り方
Jenkinsプラグインの作り方Jenkinsプラグインの作り方
Jenkinsプラグインの作り方
 
Androidリリース作業の効率化(2)
Androidリリース作業の効率化(2)Androidリリース作業の効率化(2)
Androidリリース作業の効率化(2)
 
Spock Framework
Spock FrameworkSpock Framework
Spock Framework
 
Groovy, Transforming Language
Groovy, Transforming LanguageGroovy, Transforming Language
Groovy, Transforming Language
 
Gradle a new Generation Build Tool
Gradle a new Generation Build ToolGradle a new Generation Build Tool
Gradle a new Generation Build Tool
 
function list
function listfunction list
function list
 
レガシーコード改善はじめました 横浜道場
レガシーコード改善はじめました 横浜道場レガシーコード改善はじめました 横浜道場
レガシーコード改善はじめました 横浜道場
 
Jenkins導入ライブ
Jenkins導入ライブJenkins導入ライブ
Jenkins導入ライブ
 
How about Gradle?
How about Gradle?How about Gradle?
How about Gradle?
 

Similar a Testing Web Applications with GEB

jQuery Rescue Adventure
jQuery Rescue AdventurejQuery Rescue Adventure
jQuery Rescue AdventureAllegient
 
Kotlin Basics - Apalon Kotlin Sprint Part 2
Kotlin Basics - Apalon Kotlin Sprint Part 2Kotlin Basics - Apalon Kotlin Sprint Part 2
Kotlin Basics - Apalon Kotlin Sprint Part 2Kirill Rozov
 
mobl - model-driven engineering lecture
mobl - model-driven engineering lecturemobl - model-driven engineering lecture
mobl - model-driven engineering lecturezefhemel
 
Kotlin Austin Droids April 14 2016
Kotlin Austin Droids April 14 2016Kotlin Austin Droids April 14 2016
Kotlin Austin Droids April 14 2016DesertJames
 
Productive Programming in Groovy
Productive Programming in GroovyProductive Programming in Groovy
Productive Programming in GroovyGanesh Samarthyam
 
An in-depth look at jQuery
An in-depth look at jQueryAn in-depth look at jQuery
An in-depth look at jQueryPaul Bakaus
 
mobl presentation @ IHomer
mobl presentation @ IHomermobl presentation @ IHomer
mobl presentation @ IHomerzefhemel
 
Jython: Python para la plataforma Java (EL2009)
Jython: Python para la plataforma Java (EL2009)Jython: Python para la plataforma Java (EL2009)
Jython: Python para la plataforma Java (EL2009)Leonardo Soto
 
The Dom Scripting Toolkit J Query
The Dom Scripting Toolkit J QueryThe Dom Scripting Toolkit J Query
The Dom Scripting Toolkit J QueryQConLondon2008
 
HTML5 JavaScript Interfaces
HTML5 JavaScript InterfacesHTML5 JavaScript Interfaces
HTML5 JavaScript InterfacesAaron Gustafson
 
Metaprogramming in Haskell
Metaprogramming in HaskellMetaprogramming in Haskell
Metaprogramming in HaskellHiromi Ishii
 
Swift와 Objective-C를 함께 쓰는 방법
Swift와 Objective-C를 함께 쓰는 방법Swift와 Objective-C를 함께 쓰는 방법
Swift와 Objective-C를 함께 쓰는 방법Jung Kim
 
Grails 1.2 探検隊 -新たな聖杯をもとめて・・・-
Grails 1.2 探検隊 -新たな聖杯をもとめて・・・-Grails 1.2 探検隊 -新たな聖杯をもとめて・・・-
Grails 1.2 探検隊 -新たな聖杯をもとめて・・・-Tsuyoshi Yamamoto
 
Jython: Python para la plataforma Java (JRSL 09)
Jython: Python para la plataforma Java (JRSL 09)Jython: Python para la plataforma Java (JRSL 09)
Jython: Python para la plataforma Java (JRSL 09)Leonardo Soto
 
Kamil Chmielewski, Jacek Juraszek - "Hadoop. W poszukiwaniu złotego młotka."
Kamil Chmielewski, Jacek Juraszek - "Hadoop. W poszukiwaniu złotego młotka."Kamil Chmielewski, Jacek Juraszek - "Hadoop. W poszukiwaniu złotego młotka."
Kamil Chmielewski, Jacek Juraszek - "Hadoop. W poszukiwaniu złotego młotka."sjabs
 

Similar a Testing Web Applications with GEB (20)

jQuery Rescue Adventure
jQuery Rescue AdventurejQuery Rescue Adventure
jQuery Rescue Adventure
 
Kotlin Basics - Apalon Kotlin Sprint Part 2
Kotlin Basics - Apalon Kotlin Sprint Part 2Kotlin Basics - Apalon Kotlin Sprint Part 2
Kotlin Basics - Apalon Kotlin Sprint Part 2
 
mobl - model-driven engineering lecture
mobl - model-driven engineering lecturemobl - model-driven engineering lecture
mobl - model-driven engineering lecture
 
Play á la Rails
Play á la RailsPlay á la Rails
Play á la Rails
 
Kotlin Austin Droids April 14 2016
Kotlin Austin Droids April 14 2016Kotlin Austin Droids April 14 2016
Kotlin Austin Droids April 14 2016
 
Productive Programming in Groovy
Productive Programming in GroovyProductive Programming in Groovy
Productive Programming in Groovy
 
An in-depth look at jQuery
An in-depth look at jQueryAn in-depth look at jQuery
An in-depth look at jQuery
 
mobl presentation @ IHomer
mobl presentation @ IHomermobl presentation @ IHomer
mobl presentation @ IHomer
 
Jython: Python para la plataforma Java (EL2009)
Jython: Python para la plataforma Java (EL2009)Jython: Python para la plataforma Java (EL2009)
Jython: Python para la plataforma Java (EL2009)
 
The Dom Scripting Toolkit J Query
The Dom Scripting Toolkit J QueryThe Dom Scripting Toolkit J Query
The Dom Scripting Toolkit J Query
 
Griffon @ Svwjug
Griffon @ SvwjugGriffon @ Svwjug
Griffon @ Svwjug
 
HTML5 JavaScript Interfaces
HTML5 JavaScript InterfacesHTML5 JavaScript Interfaces
HTML5 JavaScript Interfaces
 
Metaprogramming in Haskell
Metaprogramming in HaskellMetaprogramming in Haskell
Metaprogramming in Haskell
 
IN4308 Lecture 3
IN4308 Lecture 3IN4308 Lecture 3
IN4308 Lecture 3
 
Swift와 Objective-C를 함께 쓰는 방법
Swift와 Objective-C를 함께 쓰는 방법Swift와 Objective-C를 함께 쓰는 방법
Swift와 Objective-C를 함께 쓰는 방법
 
SVGo workshop
SVGo workshopSVGo workshop
SVGo workshop
 
Grails 1.2 探検隊 -新たな聖杯をもとめて・・・-
Grails 1.2 探検隊 -新たな聖杯をもとめて・・・-Grails 1.2 探検隊 -新たな聖杯をもとめて・・・-
Grails 1.2 探検隊 -新たな聖杯をもとめて・・・-
 
Jython: Python para la plataforma Java (JRSL 09)
Jython: Python para la plataforma Java (JRSL 09)Jython: Python para la plataforma Java (JRSL 09)
Jython: Python para la plataforma Java (JRSL 09)
 
Kamil Chmielewski, Jacek Juraszek - "Hadoop. W poszukiwaniu złotego młotka."
Kamil Chmielewski, Jacek Juraszek - "Hadoop. W poszukiwaniu złotego młotka."Kamil Chmielewski, Jacek Juraszek - "Hadoop. W poszukiwaniu złotego młotka."
Kamil Chmielewski, Jacek Juraszek - "Hadoop. W poszukiwaniu złotego młotka."
 
jQuery
jQueryjQuery
jQuery
 

Más de Howard Lewis Ship

Spock: A Highly Logical Way To Test
Spock: A Highly Logical Way To TestSpock: A Highly Logical Way To Test
Spock: A Highly Logical Way To TestHoward Lewis Ship
 
Modern Application Foundations: Underscore and Twitter Bootstrap
Modern Application Foundations: Underscore and Twitter BootstrapModern Application Foundations: Underscore and Twitter Bootstrap
Modern Application Foundations: Underscore and Twitter BootstrapHoward Lewis Ship
 
Have Your Cake and Eat It Too: Meta-Programming Techniques for Java
Have Your Cake and Eat It Too: Meta-Programming Techniques for JavaHave Your Cake and Eat It Too: Meta-Programming Techniques for Java
Have Your Cake and Eat It Too: Meta-Programming Techniques for JavaHoward Lewis Ship
 
Clojure: Towards The Essence Of Programming (What's Next? Conference, May 2011)
Clojure: Towards The Essence Of Programming (What's Next? Conference, May 2011)Clojure: Towards The Essence Of Programming (What's Next? Conference, May 2011)
Clojure: Towards The Essence Of Programming (What's Next? Conference, May 2011)Howard Lewis Ship
 
Arduino: Open Source Hardware Hacking from the Software Nerd Perspective
Arduino: Open Source Hardware Hacking from the Software Nerd PerspectiveArduino: Open Source Hardware Hacking from the Software Nerd Perspective
Arduino: Open Source Hardware Hacking from the Software Nerd PerspectiveHoward Lewis Ship
 
Practical Clojure Programming
Practical Clojure ProgrammingPractical Clojure Programming
Practical Clojure ProgrammingHoward Lewis Ship
 
Clojure: Towards The Essence of Programming
Clojure: Towards The Essence of ProgrammingClojure: Towards The Essence of Programming
Clojure: Towards The Essence of ProgrammingHoward Lewis Ship
 
Tapestry 5: Java Power, Scripting Ease
Tapestry 5: Java Power, Scripting EaseTapestry 5: Java Power, Scripting Ease
Tapestry 5: Java Power, Scripting EaseHoward Lewis Ship
 
Brew up a Rich Web Application with Cappuccino
Brew up a Rich Web Application with CappuccinoBrew up a Rich Web Application with Cappuccino
Brew up a Rich Web Application with CappuccinoHoward Lewis Ship
 
Clojure: Functional Concurrency for the JVM (presented at OSCON)
Clojure: Functional Concurrency for the JVM (presented at OSCON)Clojure: Functional Concurrency for the JVM (presented at OSCON)
Clojure: Functional Concurrency for the JVM (presented at OSCON)Howard Lewis Ship
 
Tapestry: State of the Union
Tapestry: State of the UnionTapestry: State of the Union
Tapestry: State of the UnionHoward Lewis Ship
 
Clojure: Functional Concurrency for the JVM (presented at Open Source Bridge)
Clojure: Functional Concurrency for the JVM (presented at Open Source Bridge)Clojure: Functional Concurrency for the JVM (presented at Open Source Bridge)
Clojure: Functional Concurrency for the JVM (presented at Open Source Bridge)Howard Lewis Ship
 

Más de Howard Lewis Ship (16)

Spock: A Highly Logical Way To Test
Spock: A Highly Logical Way To TestSpock: A Highly Logical Way To Test
Spock: A Highly Logical Way To Test
 
Modern Application Foundations: Underscore and Twitter Bootstrap
Modern Application Foundations: Underscore and Twitter BootstrapModern Application Foundations: Underscore and Twitter Bootstrap
Modern Application Foundations: Underscore and Twitter Bootstrap
 
Have Your Cake and Eat It Too: Meta-Programming Techniques for Java
Have Your Cake and Eat It Too: Meta-Programming Techniques for JavaHave Your Cake and Eat It Too: Meta-Programming Techniques for Java
Have Your Cake and Eat It Too: Meta-Programming Techniques for Java
 
Clojure: Towards The Essence Of Programming (What's Next? Conference, May 2011)
Clojure: Towards The Essence Of Programming (What's Next? Conference, May 2011)Clojure: Towards The Essence Of Programming (What's Next? Conference, May 2011)
Clojure: Towards The Essence Of Programming (What's Next? Conference, May 2011)
 
Arduino: Open Source Hardware Hacking from the Software Nerd Perspective
Arduino: Open Source Hardware Hacking from the Software Nerd PerspectiveArduino: Open Source Hardware Hacking from the Software Nerd Perspective
Arduino: Open Source Hardware Hacking from the Software Nerd Perspective
 
Practical Clojure Programming
Practical Clojure ProgrammingPractical Clojure Programming
Practical Clojure Programming
 
Clojure: Towards The Essence of Programming
Clojure: Towards The Essence of ProgrammingClojure: Towards The Essence of Programming
Clojure: Towards The Essence of Programming
 
Codemash-Clojure.pdf
Codemash-Clojure.pdfCodemash-Clojure.pdf
Codemash-Clojure.pdf
 
Codemash-Tapestry.pdf
Codemash-Tapestry.pdfCodemash-Tapestry.pdf
Codemash-Tapestry.pdf
 
Tapestry 5: Java Power, Scripting Ease
Tapestry 5: Java Power, Scripting EaseTapestry 5: Java Power, Scripting Ease
Tapestry 5: Java Power, Scripting Ease
 
Brew up a Rich Web Application with Cappuccino
Brew up a Rich Web Application with CappuccinoBrew up a Rich Web Application with Cappuccino
Brew up a Rich Web Application with Cappuccino
 
Clojure Deep Dive
Clojure Deep DiveClojure Deep Dive
Clojure Deep Dive
 
Clojure: Functional Concurrency for the JVM (presented at OSCON)
Clojure: Functional Concurrency for the JVM (presented at OSCON)Clojure: Functional Concurrency for the JVM (presented at OSCON)
Clojure: Functional Concurrency for the JVM (presented at OSCON)
 
Cascade
CascadeCascade
Cascade
 
Tapestry: State of the Union
Tapestry: State of the UnionTapestry: State of the Union
Tapestry: State of the Union
 
Clojure: Functional Concurrency for the JVM (presented at Open Source Bridge)
Clojure: Functional Concurrency for the JVM (presented at Open Source Bridge)Clojure: Functional Concurrency for the JVM (presented at Open Source Bridge)
Clojure: Functional Concurrency for the JVM (presented at Open Source Bridge)
 

Último

Apidays Singapore 2024 - Building Digital Trust in a Digital Economy by Veron...
Apidays Singapore 2024 - Building Digital Trust in a Digital Economy by Veron...Apidays Singapore 2024 - Building Digital Trust in a Digital Economy by Veron...
Apidays Singapore 2024 - Building Digital Trust in a Digital Economy by Veron...apidays
 
presentation ICT roal in 21st century education
presentation ICT roal in 21st century educationpresentation ICT roal in 21st century education
presentation ICT roal in 21st century educationjfdjdjcjdnsjd
 
Boost PC performance: How more available memory can improve productivity
Boost PC performance: How more available memory can improve productivityBoost PC performance: How more available memory can improve productivity
Boost PC performance: How more available memory can improve productivityPrincipled Technologies
 
The 7 Things I Know About Cyber Security After 25 Years | April 2024
The 7 Things I Know About Cyber Security After 25 Years | April 2024The 7 Things I Know About Cyber Security After 25 Years | April 2024
The 7 Things I Know About Cyber Security After 25 Years | April 2024Rafal Los
 
GenAI Risks & Security Meetup 01052024.pdf
GenAI Risks & Security Meetup 01052024.pdfGenAI Risks & Security Meetup 01052024.pdf
GenAI Risks & Security Meetup 01052024.pdflior mazor
 
2024: Domino Containers - The Next Step. News from the Domino Container commu...
2024: Domino Containers - The Next Step. News from the Domino Container commu...2024: Domino Containers - The Next Step. News from the Domino Container commu...
2024: Domino Containers - The Next Step. News from the Domino Container commu...Martijn de Jong
 
Workshop - Best of Both Worlds_ Combine KG and Vector search for enhanced R...
Workshop - Best of Both Worlds_ Combine  KG and Vector search for  enhanced R...Workshop - Best of Both Worlds_ Combine  KG and Vector search for  enhanced R...
Workshop - Best of Both Worlds_ Combine KG and Vector search for enhanced R...Neo4j
 
Understanding Discord NSFW Servers A Guide for Responsible Users.pdf
Understanding Discord NSFW Servers A Guide for Responsible Users.pdfUnderstanding Discord NSFW Servers A Guide for Responsible Users.pdf
Understanding Discord NSFW Servers A Guide for Responsible Users.pdfUK Journal
 
Top 5 Benefits OF Using Muvi Live Paywall For Live Streams
Top 5 Benefits OF Using Muvi Live Paywall For Live StreamsTop 5 Benefits OF Using Muvi Live Paywall For Live Streams
Top 5 Benefits OF Using Muvi Live Paywall For Live StreamsRoshan Dwivedi
 
MINDCTI Revenue Release Quarter One 2024
MINDCTI Revenue Release Quarter One 2024MINDCTI Revenue Release Quarter One 2024
MINDCTI Revenue Release Quarter One 2024MIND CTI
 
How to Troubleshoot Apps for the Modern Connected Worker
How to Troubleshoot Apps for the Modern Connected WorkerHow to Troubleshoot Apps for the Modern Connected Worker
How to Troubleshoot Apps for the Modern Connected WorkerThousandEyes
 
Bajaj Allianz Life Insurance Company - Insurer Innovation Award 2024
Bajaj Allianz Life Insurance Company - Insurer Innovation Award 2024Bajaj Allianz Life Insurance Company - Insurer Innovation Award 2024
Bajaj Allianz Life Insurance Company - Insurer Innovation Award 2024The Digital Insurer
 
Connector Corner: Accelerate revenue generation using UiPath API-centric busi...
Connector Corner: Accelerate revenue generation using UiPath API-centric busi...Connector Corner: Accelerate revenue generation using UiPath API-centric busi...
Connector Corner: Accelerate revenue generation using UiPath API-centric busi...DianaGray10
 
Top 10 Most Downloaded Games on Play Store in 2024
Top 10 Most Downloaded Games on Play Store in 2024Top 10 Most Downloaded Games on Play Store in 2024
Top 10 Most Downloaded Games on Play Store in 2024SynarionITSolutions
 
Deploy with confidence: VMware Cloud Foundation 5.1 on next gen Dell PowerEdg...
Deploy with confidence: VMware Cloud Foundation 5.1 on next gen Dell PowerEdg...Deploy with confidence: VMware Cloud Foundation 5.1 on next gen Dell PowerEdg...
Deploy with confidence: VMware Cloud Foundation 5.1 on next gen Dell PowerEdg...Principled Technologies
 
Mastering MySQL Database Architecture: Deep Dive into MySQL Shell and MySQL R...
Mastering MySQL Database Architecture: Deep Dive into MySQL Shell and MySQL R...Mastering MySQL Database Architecture: Deep Dive into MySQL Shell and MySQL R...
Mastering MySQL Database Architecture: Deep Dive into MySQL Shell and MySQL R...Miguel Araújo
 
Apidays New York 2024 - The value of a flexible API Management solution for O...
Apidays New York 2024 - The value of a flexible API Management solution for O...Apidays New York 2024 - The value of a flexible API Management solution for O...
Apidays New York 2024 - The value of a flexible API Management solution for O...apidays
 
A Domino Admins Adventures (Engage 2024)
A Domino Admins Adventures (Engage 2024)A Domino Admins Adventures (Engage 2024)
A Domino Admins Adventures (Engage 2024)Gabriella Davis
 
HTML Injection Attacks: Impact and Mitigation Strategies
HTML Injection Attacks: Impact and Mitigation StrategiesHTML Injection Attacks: Impact and Mitigation Strategies
HTML Injection Attacks: Impact and Mitigation StrategiesBoston Institute of Analytics
 
Artificial Intelligence: Facts and Myths
Artificial Intelligence: Facts and MythsArtificial Intelligence: Facts and Myths
Artificial Intelligence: Facts and MythsJoaquim Jorge
 

Último (20)

Apidays Singapore 2024 - Building Digital Trust in a Digital Economy by Veron...
Apidays Singapore 2024 - Building Digital Trust in a Digital Economy by Veron...Apidays Singapore 2024 - Building Digital Trust in a Digital Economy by Veron...
Apidays Singapore 2024 - Building Digital Trust in a Digital Economy by Veron...
 
presentation ICT roal in 21st century education
presentation ICT roal in 21st century educationpresentation ICT roal in 21st century education
presentation ICT roal in 21st century education
 
Boost PC performance: How more available memory can improve productivity
Boost PC performance: How more available memory can improve productivityBoost PC performance: How more available memory can improve productivity
Boost PC performance: How more available memory can improve productivity
 
The 7 Things I Know About Cyber Security After 25 Years | April 2024
The 7 Things I Know About Cyber Security After 25 Years | April 2024The 7 Things I Know About Cyber Security After 25 Years | April 2024
The 7 Things I Know About Cyber Security After 25 Years | April 2024
 
GenAI Risks & Security Meetup 01052024.pdf
GenAI Risks & Security Meetup 01052024.pdfGenAI Risks & Security Meetup 01052024.pdf
GenAI Risks & Security Meetup 01052024.pdf
 
2024: Domino Containers - The Next Step. News from the Domino Container commu...
2024: Domino Containers - The Next Step. News from the Domino Container commu...2024: Domino Containers - The Next Step. News from the Domino Container commu...
2024: Domino Containers - The Next Step. News from the Domino Container commu...
 
Workshop - Best of Both Worlds_ Combine KG and Vector search for enhanced R...
Workshop - Best of Both Worlds_ Combine  KG and Vector search for  enhanced R...Workshop - Best of Both Worlds_ Combine  KG and Vector search for  enhanced R...
Workshop - Best of Both Worlds_ Combine KG and Vector search for enhanced R...
 
Understanding Discord NSFW Servers A Guide for Responsible Users.pdf
Understanding Discord NSFW Servers A Guide for Responsible Users.pdfUnderstanding Discord NSFW Servers A Guide for Responsible Users.pdf
Understanding Discord NSFW Servers A Guide for Responsible Users.pdf
 
Top 5 Benefits OF Using Muvi Live Paywall For Live Streams
Top 5 Benefits OF Using Muvi Live Paywall For Live StreamsTop 5 Benefits OF Using Muvi Live Paywall For Live Streams
Top 5 Benefits OF Using Muvi Live Paywall For Live Streams
 
MINDCTI Revenue Release Quarter One 2024
MINDCTI Revenue Release Quarter One 2024MINDCTI Revenue Release Quarter One 2024
MINDCTI Revenue Release Quarter One 2024
 
How to Troubleshoot Apps for the Modern Connected Worker
How to Troubleshoot Apps for the Modern Connected WorkerHow to Troubleshoot Apps for the Modern Connected Worker
How to Troubleshoot Apps for the Modern Connected Worker
 
Bajaj Allianz Life Insurance Company - Insurer Innovation Award 2024
Bajaj Allianz Life Insurance Company - Insurer Innovation Award 2024Bajaj Allianz Life Insurance Company - Insurer Innovation Award 2024
Bajaj Allianz Life Insurance Company - Insurer Innovation Award 2024
 
Connector Corner: Accelerate revenue generation using UiPath API-centric busi...
Connector Corner: Accelerate revenue generation using UiPath API-centric busi...Connector Corner: Accelerate revenue generation using UiPath API-centric busi...
Connector Corner: Accelerate revenue generation using UiPath API-centric busi...
 
Top 10 Most Downloaded Games on Play Store in 2024
Top 10 Most Downloaded Games on Play Store in 2024Top 10 Most Downloaded Games on Play Store in 2024
Top 10 Most Downloaded Games on Play Store in 2024
 
Deploy with confidence: VMware Cloud Foundation 5.1 on next gen Dell PowerEdg...
Deploy with confidence: VMware Cloud Foundation 5.1 on next gen Dell PowerEdg...Deploy with confidence: VMware Cloud Foundation 5.1 on next gen Dell PowerEdg...
Deploy with confidence: VMware Cloud Foundation 5.1 on next gen Dell PowerEdg...
 
Mastering MySQL Database Architecture: Deep Dive into MySQL Shell and MySQL R...
Mastering MySQL Database Architecture: Deep Dive into MySQL Shell and MySQL R...Mastering MySQL Database Architecture: Deep Dive into MySQL Shell and MySQL R...
Mastering MySQL Database Architecture: Deep Dive into MySQL Shell and MySQL R...
 
Apidays New York 2024 - The value of a flexible API Management solution for O...
Apidays New York 2024 - The value of a flexible API Management solution for O...Apidays New York 2024 - The value of a flexible API Management solution for O...
Apidays New York 2024 - The value of a flexible API Management solution for O...
 
A Domino Admins Adventures (Engage 2024)
A Domino Admins Adventures (Engage 2024)A Domino Admins Adventures (Engage 2024)
A Domino Admins Adventures (Engage 2024)
 
HTML Injection Attacks: Impact and Mitigation Strategies
HTML Injection Attacks: Impact and Mitigation StrategiesHTML Injection Attacks: Impact and Mitigation Strategies
HTML Injection Attacks: Impact and Mitigation Strategies
 
Artificial Intelligence: Facts and Myths
Artificial Intelligence: Facts and MythsArtificial Intelligence: Facts and Myths
Artificial Intelligence: Facts and Myths
 

Testing Web Applications with GEB

  • 1. Testing Web Applications with GEB Howard M. Lewis Ship TWD Consulting hlship@gmail.com @hlship © 2012 Howard M. Lewis Ship
  • 2. Testing Web Applications What's your process? Manual? Selenium? What about Ajax?
  • 3. Spoiled by jQuery <div class="summary"> … <div class="subtotal">107.95</div> $(".order-summary .subtotal").text() == "107.95" http://jquery.com/
  • 4. Selenium WebDriver Drives: or limited HtmlUnit (browserless) http://seleniumhq.org/
  • 5. WebDriver driver = new FirefoxDriver(); driver.get("http://localhost:8080/order-summary/12345"); WebElement element = driver.findElement( By.cssSelector(".order-summary .subtotal"); assertEquals(element.getText(), "107.95");
  • 6. driver.get("http://www.google.com"); WebElement element = driver.findElement(By.name("q")); element.sendKeys("Cheese!"); element.submit(); Triggers Ajax Update new WebDriverWait(driver, 10)).until(new ExpectedCondition<Boolean>() { public Boolean apply(WebDriver d) { return d.getTitle().toLowerCase().startsWith("cheese!"); } };
  • 7. Java == High Ceremony
  • 8. Groovy == Low Ceremony + Dynamic
  • 9. Power of Selenium WebDriver 2.15.0 Elegance of jQuery content selection Robustness of Page Object modelling Expressiveness of Groovy First Class Documentation
  • 10. Running GEB Interactively geb.groovy import groovy.grape.Grape Grape.grab([group:'org.codehaus.geb', module:'geb-core', version:'0.6.3']) Grape.grab([group:'org.seleniumhq.selenium', module:'selenium-firefox-driver', version:'2.15.0']) Grape.grab([group:'org.seleniumhq.selenium', module:'selenium-support', ⏎ version:'2.15.0']) import geb.* import java.util.logging.* new File("geb-logging.properties").withInputStream { ⏎ LogManager.logManager.readConfiguration it } geb-logging.properties handlers=java.util.logging.ConsoleHandler java.util.logging.ConsoleHandler.level=WARNING java.util.logging.ConsoleHandler.formatter=java.util.logging.SimpleFormatter
  • 11. GEB Interactive $ groovysh Groovy Shell (1.8.6, JVM: 1.6.0_29) Type 'help' or 'h' for help. ------------------------------------------------- groovy:000> . geb.groovy ===> [import groovy.grape.Grape] ===> null ===> null ===> null ===> [import groovy.grape.Grape, import geb.*] ===> [import groovy.grape.Grape, import geb.*, import ⏎ java.util.logging.*] ===> null groovy:000> b = new Browser(baseUrl: "http://google.com/") ===> geb.Browser@190a0d51 groovy:000> b.go() ===> null groovy:000>
  • 13. Google Demo b = new Browser(baseUrl: "http://google.com/") b.go() b.$("input", name:"q") << "geb" b.waitFor { b.title.toLowerCase().startsWith("geb") } b.$("a.l").text() b.$("a.l")*.text() b.$("a.l")*.@href b.$("input", name:"q") << "geb groovy"  b.$("a.l").first().click() b.quit() Not needed in 0.7
  • 15. IMDB Demo b = new Browser() b.baseUrl = "http://imdb.com" b.go() b.$("#navbar-query") << "Bladerunner" b.$("#navbar-submit-button").click() b.waitFor { b.title == "IMDb Search" } b.$("#main table", 1).find("a")*.text() b.$("#main table", 1).find("a", text:"Blade Runner").click() assert b.$(".star-box-giga-star").text() == "9.9" b.$("table.cast_list td.name a")[0..3]*.text()
  • 16. Browser Page Navigator go() : void Delegation pageUrl : String x : int quit() : void title : String y : int page : Page $(…) : Navigator width : int waitFor(…) : Object height : int startsWith(…) : TextMatcher disabled : boolean contains(…) : TextMatcher empty : boolean Delegation endsWith(…) : TextMatcher displayed : boolean add(String) : Navigator click() : void filter(...) : Navigator GebSpec find(…) : Navigator not(String) : Navigator first() : Navigator last() : Navigator getAt(…) : Navigator getAttribute(String) : String has(String) : Navigator parents(String) : Navigator parentsUntil(String): Navigator size() : int text() : String value() : Object
  • 17. $(css selector, index, attribute / text matchers) $("p") ➠ all <p> CS $("p", 3) ➠ 4th <p> S3 $("p")[3] ➠ 4th <p> $("p")[0..2] ➠ 1st through 3rd <p>
  • 18. Attribute Matchers $("a", text:"Blade Runner") ➠ All <a> tags whose text is "Blade Runner" $("a", href: contains("/name/") ➠ All <a> tags whose href attribute contains "/name/" $("a", href: ~/nm[0-9]+/) ➠ All <a> tags whose href attribute matches the pattern
  • 19. Attribute Predicates Case Sensitive Case Insensitive Description startsWith iStartsWith start with value contains iContains contains the value anywhere endsWith iEndsWith end with value contains value surrounded by whitespace (or at constainsWord iContainsWord begin or end) notStartsWith iNotStartsWith DOES NOT start with value notContains iNotContains DOES NOT contain value anywhere notEndsWith iNotEndsWith DOES NOT end with value DOES NOT contain value (surrounded by notContainsWord iNotContainsWord whitespace, or at begin or end)
  • 20. Relative Traversal <div class="a">     <div class="b">         <p class="c"></p>         <p class="d"></p>         <p class="e"></p>     </div>     <div class="f"></div> </div> $("p.d").previous() p.c $("p.e").prevAll() p.c p.d $("p.d").next() p.e $("p.c").nextAll() p.d p.e $("p.cd").parent() div.b $("p.c").siblings() p.d p.e $("div.a").children() div.b div.f
  • 21. Navigators are Groovy Collections each() is a Groovy Collection method groovy:000> castList = [:] ===> {} groovy:000> b.$("table.cast_list tr").tail().each { castList[it.find("td.name").text()] = it.find("td.character").text() } ===> […] groovy:000> castList ===> {Harrison Ford=Rick Deckard, Rutger Hauer=Roy Batty, Sean Young=Rachael, Edward James Olmos=Gaff, M. Emmet Walsh=Bryant, Daryl Hannah=Pris, William Sanderson=J.F. Sebastian, Brion James=Leon Kowalski, Joe Turkel=Dr. Eldon Tyrell, Joanna Cassidy=Zhora, James Hong=Hannibal Chew, Morgan Paull=Holden, Kevin Thompson=Bear, John Edward Allen=Kaiser, Hy Pyke=Taffey Lewis} http://groovy.codehaus.org/groovy-jdk/java/util/Collection.html
  • 22. Forms <form id="navbar-form" … <input type="text" name="q" … groovy:000> b.q = "Galaxy Quest" ===> Galaxy Quest groovy:000> b.$("#navbar-form").q ===> Galaxy Quest
  • 24. Problem: Repetition $("a", text:"Contact Us").click() waitFor { b.title == "Contact Us" } $(".alert .btn-primary").click() waitFor { b.title == "Contact Us" } ($(".search-form input[name='query']") << "search term").submit()
  • 25. Solution: Model Pages not just DOM Browser Page go() : void Delegation pageUrl : String quit() : void title : String page : Page $(…) : Navigator at(…) : boolean waitFor(…) : Object verifyAt() : boolean browser: Browser class Home extends geb.Page { static url = "" static at = { title == "The Internet Movie Database (IMDb)" } } groovy:000> b.$(".home").click(Home) ===> null groovy:000> b.verifyAt() ===> true groovy:000> b.at Home ===> true groovy:000> b.page ===> Home groovy:000> b.to Home ===> null
  • 26. Page Content class Home extends Page { static at = { title == "The Internet Movie Database (IMDb)" } static url = "" static content = { boxOffice { $("h3", text:"Box Office").parent() } firstBoxOffice { boxOffice.find("a").first() } } } groovy:000> b.firstBoxOffice.click() ===> null
  • 27. to / do / at groovy:000> b.to Home ===> null groovy:000> b.q = "Forbidden Planet" ===> Forbidden Planet groovy:000> b.searchForm.go.click() ===> null groovy:000> b.at Search ===> true groovy:000>
  • 28. Content Options static content = { boxOffice { $("h3", text:"Box Office").parent() } boxOfficeLinks { boxOffice.find("a", text: iNotStartsWith("see more")) } movieShowtimes(required:false) { $("h3", text:"Movie Showtimes").parent() } movieShowtimesGo(required:false) { movieShowtimes.find("input", value:"Go") } }
  • 29. Content Options Option Type Default Description Evaluate content once, or cache boolean false on each access Error on page load if content does not exist (use required boolean true false for optional or Ajax- loaded) Page or Class, On a link, identify the page to null list of Page or Class the link submits to Wait for content to become wait varies null available (via Ajax/DHTML)
  • 30. Page Methods class Home extends Page { static at = { title == "The Internet Movie Database (IMDb)" } static url = "" static content = { boxOffice { $("h3", text:"Box Office").parent() } boxOfficeLinks { boxOffice.find("a", text: iNotStartsWith("see more")) } } def clickBoxOffice(index) { def link = boxOfficeLinks[index] def label = link.text() link.click() waitFor { title.startsWith(label) } } } groovy:000> b.to Home ===> null groovy:000> b.clickBoxOffice 0 ===> true groovy:000>
  • 31. Problem: Re-used web pages class Movie extends Page { static at = { assert title.startsWith("Blade Runner") true GEB 0.7 will magically } convert to asserts static content = { rating { $(".star-box-gig-start").text() } castList { $("table.cast_list tr").tail() } } } groovy:000> b.$("#main table", 2).find("a",7).click(Movie) ===> null groovy:000> b.page ===> Movie groovy:000> b.verifyAt() ERROR org.codehaus.groovy.runtime.powerassert.PowerAssertionError: assert title.startsWith("Blade Runner") | | | false The Bugs Bunny/Road-Runner Movie (1979) - IMDb
  • 32. Solution: Configured Page Instances class Movie extends Page { String expectedTitle static at = { assert title.startsWith expectedTitle true } static content = { rating { $(".star-box-gig-start").text() } castList { $("table.cast_list tr").tail() } } }
  • 33. class Search extends Page { static at = { assert title == "IMDb Search" true } static content = { mainTable { $("#main table") } matchingTitles { mainTable[2] } matchingTitlesLinks { matchingTitles.find("a", ⏎ href: contains("/title/tt")) } } def clickMatchingTitle(int index) { def link = matchingTitlesLinks[index] def label = link.text() link.click() browser.at new Movie(expectedTitle: label) } }
  • 34. click() groovy:000> b.searchForm.go.click() ===> null groovy:000> b.clickMatchingTitle 3 ===> true def clickMatchingTitle(int index) { def link = matchingTitlesLinks[index] def label = link.text() link.click() browser.at new Movie(expectedTitle: label) }
  • 35. Problem: Duplication on Pages b.$("#navbar-query") << "Bladerunner" b.$("#navbar-submit-button").click() Other examples: Login / Logout / Register "Contact Us" & other boilerplate "Mark Favorite" View Product Details Bid / Buy
  • 36. Solution: Modules class SearchForm extends geb.Module { static content = { field { $("#navbar-query") } go(to: Search) { $("#navbar-submit-button") } } } class Home extends Page { static at = { title == "The Internet Movie Database (IMDb)" } static url = "" static content = { searchForm { module SearchForm } } } groovy:000> b.to Home ===> null groovy:000> b.searchForm.field << "Serenity" ===> [org.openqa.selenium.firefox.FirefoxWebElement@1ef44b1f] groovy:000> b.searchForm.go.click() ===> null groovy:000> b.page ===> Search
  • 37. Problem: Repeating Elements <table class="cast_list"> <tr> <td class="primary_photo"> … <td class="name"> … <td class="ellipsis"> … <td class="character"> …
  • 38. Solution: Module Lists class CastRow extends Module { static content = { actorName { $("td.name").text() } characterName { $("td.character").text() } } } Scope limited to each <tr> class Movie extends Page { String expectedTitle static at = { title.startsWith expectedTitle } static content = { rating { $(".star-box-gig-start").text() } castList { moduleList CastRow, $("table.cast_list tr").tail() } } }
  • 39. groovy:000> b.at(new Movie(expectedTitle: "Blade Runner")) ===> true groovy:000> b.castList[0].actorName ===> Harrison Ford groovy:000> b.castList[0].characterName ===> Rick Deckard groovy:000> b.castList*.actorName ===> [Harrison Ford, Rutger Hauer, Sean Young, Edward James Olmos, ⏎ M. Emmet Walsh, Daryl Hannah, William Sanderson, Brion James, Joe Turkel, ⏎ Joanna Cassidy, James Hong, Morgan Paull, Kevin Thompson, John Edward Allen, ⏎ Hy Pyke]
  • 41. js object groovy:000> b = new Browser(baseUrl: "http://jquery.org") ===> geb.Browser@5ec22978 groovy:000> b.go() ===> null groovy:000> b.js."document.title" ===> jQuery Project Access simple page properties
  • 42. Executing JavaScript Text evaluated in-browser groovy:000> b.js.exec ''' groovy:001> $("img").css("background-color", "red").fadeOut() groovy:002> ''' ===> null groovy:000> b.js.exec 1, 2, "return arguments[0] + arguments[1];" ===> 3
  • 43. jQuery Hook More methodMissing() magic! groovy:000> b.$("img").jquery.fadeIn() ===> [org.openqa.selenium.firefox.FirefoxWebElement@de86fd70, org.openqa.selenium.firefox.FirefoxWebElement@615e6612, Silently fails unless jQuery on page org.openqa.selenium.firefox.FirefoxWebElement@52c13174, org.openqa.selenium.firefox.FirefoxWebElement@69e1ba19, org.openqa.selenium.firefox.FirefoxWebElement@2797f147, org.openqa.selenium.firefox.FirefoxWebElement@69cfbbda, org.openqa.selenium.firefox.FirefoxWebElement@27d5741a, org.openqa.selenium.firefox.FirefoxWebElement@10b36232, org.openqa.selenium.firefox.FirefoxWebElement@ec3e243f]
  • 44. Waiting waitFor { condition } waitFor timeout { condition } waitFor timeout, interval { condition } waitFor "preset" { condition }
  • 46. Reporting package myapp.tests Base class that reports at end of each test import geb.spock.* class Login extends GebReportingSpec { def "successful login"() { when: go "login" username = "user1" report "login screen" Capture HTML and screenshot login().click() then: title == "Welcome, User1" } } reports/myapp/tests/Login/1-1-login-login screen.html reports/myapp/tests/Login/1-1-login-login screen.png reports/myapp/tests/Login/1-2-login-end.html reports/myapp/tests/Login/1-2-login-end.png
  • 47. Base Classes Report end of each test Framework Artifact Base Class Reporting Base Class Spock geb-spock geb.spock.GebSpec geb.spock.GebReportingSpec Junit 4 geb-juni4 geb.junit4.GebTest geb.junit4.GebReportingTest Junit 3 geb-junit3 geb.junit3.GebTest geb.junit3.GebReportingTest TestNG geb-testng geb.testng.GebTest geb.testng.GebReportingTest Report failures only
  • 48. Delegation package myapp.tests Page import geb.spock.* Delegation class Login extends GebReportingSpec { def "successful login"() { Browser when: Delegation go "login" username = "user1" report "login screen" login().click() Geb[Reporting]Spec then: title == "Welcome, User1" } }
  • 50. GebConfig.groovy src/test/resources/GebConfig.groovy import org.openqa.selenium.firefox.FirefoxDriver import org.openqa.selenium.chrome.ChromeDriver driver = { new FirefoxDriver() } // use firefox by default waiting {     timeout = 2 // default wait is two seconds } environments {     chrome {         driver = { new ChromeDriver() }     } } http://groovy.codehaus.org/gapi/groovy/util/ConfigSlurper.html
  • 51. Environment $ grade test -Dgeb.env=chrome src/test/resources/GebConfig.groovy import org.openqa.selenium.firefox.FirefoxDriver import org.openqa.selenium.chrome.ChromeDriver driver = { new FirefoxDriver() } // use firefox by default waiting {     timeout = 2 // default wait is two seconds } environments {     chrome {         driver = { new ChromeDriver() }     } }
  • 52. Waiting Configuration GebConfig.groovy waiting { timeout = 10 retryInterval = 0.5 presets { slow { timeout = 20, retryInterval = 1 } quick { timeout = 1 } } }
  • 57. Q&A