SlideShare a Scribd company logo
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

More Related Content

PPTX
Libraries and their Role in Open Access: Challenges and Opportunities
PPTX
Web mining
PDF
Conceptual foundations of text mining and preprocessing steps nfaoui el_habib
PPTX
Evolution & Future Of Artificial Intelligence
PDF
Introduction to Artificial Intelligence and Machine Learning
PDF
Art-Making Generative AI and Instructional Design Work: An Early Brainstorm
PDF
Edge AI Smart Manufacturing - Defect Detection and Beyond (GTC 2019)
PPTX
Use of Artificial Intelligence for Literature Screening
Libraries and their Role in Open Access: Challenges and Opportunities
Web mining
Conceptual foundations of text mining and preprocessing steps nfaoui el_habib
Evolution & Future Of Artificial Intelligence
Introduction to Artificial Intelligence and Machine Learning
Art-Making Generative AI and Instructional Design Work: An Early Brainstorm
Edge AI Smart Manufacturing - Defect Detection and Beyond (GTC 2019)
Use of Artificial Intelligence for Literature Screening

Viewers also liked (20)

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

Similar to Testing Web Applications with GEB (20)

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

More from Howard Lewis Ship (17)

PDF
Spock: A Highly Logical Way To Test
PDF
Backbone.js: Run your Application Inside The Browser
PDF
Modern Application Foundations: Underscore and Twitter Bootstrap
KEY
Have Your Cake and Eat It Too: Meta-Programming Techniques for Java
PDF
Clojure: Towards The Essence Of Programming (What's Next? Conference, May 2011)
PDF
Arduino: Open Source Hardware Hacking from the Software Nerd Perspective
PDF
Practical Clojure Programming
PDF
Clojure: Towards The Essence of Programming
PDF
Codemash-Clojure.pdf
PDF
Codemash-Tapestry.pdf
PDF
Tapestry 5: Java Power, Scripting Ease
PDF
Brew up a Rich Web Application with Cappuccino
PDF
Clojure Deep Dive
PDF
Clojure: Functional Concurrency for the JVM (presented at OSCON)
PDF
PDF
Tapestry: State of the Union
ZIP
Clojure: Functional Concurrency for the JVM (presented at Open Source Bridge)
Spock: A Highly Logical Way To Test
Backbone.js: Run your Application Inside The Browser
Modern Application Foundations: Underscore and Twitter Bootstrap
Have Your Cake and Eat It Too: Meta-Programming Techniques for Java
Clojure: Towards The Essence Of Programming (What's Next? Conference, May 2011)
Arduino: Open Source Hardware Hacking from the Software Nerd Perspective
Practical Clojure Programming
Clojure: Towards The Essence of Programming
Codemash-Clojure.pdf
Codemash-Tapestry.pdf
Tapestry 5: Java Power, Scripting Ease
Brew up a Rich Web Application with Cappuccino
Clojure Deep Dive
Clojure: Functional Concurrency for the JVM (presented at OSCON)
Tapestry: State of the Union
Clojure: Functional Concurrency for the JVM (presented at Open Source Bridge)

Recently uploaded (20)

PDF
Video forgery: An extensive analysis of inter-and intra-frame manipulation al...
PDF
gpt5_lecture_notes_comprehensive_20250812015547.pdf
PDF
Agricultural_Statistics_at_a_Glance_2022_0.pdf
PDF
Diabetes mellitus diagnosis method based random forest with bat algorithm
PDF
Spectral efficient network and resource selection model in 5G networks
PPTX
Programs and apps: productivity, graphics, security and other tools
PDF
MIND Revenue Release Quarter 2 2025 Press Release
PDF
Unlocking AI with Model Context Protocol (MCP)
PDF
Heart disease approach using modified random forest and particle swarm optimi...
PPTX
Group 1 Presentation -Planning and Decision Making .pptx
PPTX
SOPHOS-XG Firewall Administrator PPT.pptx
PDF
Mushroom cultivation and it's methods.pdf
PDF
7 ChatGPT Prompts to Help You Define Your Ideal Customer Profile.pdf
PDF
A comparative analysis of optical character recognition models for extracting...
PDF
Building Integrated photovoltaic BIPV_UPV.pdf
PPTX
A Presentation on Artificial Intelligence
PPTX
KOM of Painting work and Equipment Insulation REV00 update 25-dec.pptx
PDF
Empathic Computing: Creating Shared Understanding
PPTX
cloud_computing_Infrastucture_as_cloud_p
PPTX
1. Introduction to Computer Programming.pptx
Video forgery: An extensive analysis of inter-and intra-frame manipulation al...
gpt5_lecture_notes_comprehensive_20250812015547.pdf
Agricultural_Statistics_at_a_Glance_2022_0.pdf
Diabetes mellitus diagnosis method based random forest with bat algorithm
Spectral efficient network and resource selection model in 5G networks
Programs and apps: productivity, graphics, security and other tools
MIND Revenue Release Quarter 2 2025 Press Release
Unlocking AI with Model Context Protocol (MCP)
Heart disease approach using modified random forest and particle swarm optimi...
Group 1 Presentation -Planning and Decision Making .pptx
SOPHOS-XG Firewall Administrator PPT.pptx
Mushroom cultivation and it's methods.pdf
7 ChatGPT Prompts to Help You Define Your Ideal Customer Profile.pdf
A comparative analysis of optical character recognition models for extracting...
Building Integrated photovoltaic BIPV_UPV.pdf
A Presentation on Artificial Intelligence
KOM of Painting work and Equipment Insulation REV00 update 25-dec.pptx
Empathic Computing: Creating Shared Understanding
cloud_computing_Infrastucture_as_cloud_p
1. Introduction to Computer Programming.pptx

Testing Web Applications with GEB

  • 1. Testing Web Applications with GEB Howard M. Lewis Ship TWD Consulting [email protected] @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