Web Automation using Groovy, WebDriver,
     JQuery and Page Object Model


                        By: Gaurav Bansal
Need…
• We need a solution that is based on technology
  that is native to WebBrowsers.
• We need a solution that requires no boilerplate
  code.
• We need a solution that offers a powerful set of
  tools for matching a set of elements in a
  document.
• We need a solution that offers in-built
  mechanism to incorporate Page-object model
Solution…




  GEB
What is Geb?
• Geb is a browser automation solution.
• You can use it for…
  – Acceptance Testing Web Applications
  – Automating Web Sites
  – Screen Scraping
• It brings together the…
  –   Cross browser automation capabilities of WebDriver
  –   Elegance of jQuery content selection
  –   Expressiveness of the Groovy language
  –   Robustness of Page Object modelling
About the Project
• Free Open Source, Apache License, Version
  2.0.
• Currently at version 0.7.0.
  – Home Page — http://www.gebish.org
  – The Book of Geb — http://www.gebish.org
    /manual/current
  – Source Code — https://github.com/geb/geb
  – User Mailing List — http://
    xircles.codehaus.org/projects/geb/lists
  – In Maven Central — http://
    mvnrepository.com/artifact/org.codehaus.geb
Project Components
• The heart is the geb-core component which is
  all you really need (plus WebDriver).
• For testing, you probably also want one of these
  as well:
  –   geb-spock
  –   geb-junit3
  –   geb-junit4
  –   geb-testng
  –   geb-easyb
• (Has been used from Cucumber as well).
• There is also a Grails plugin.
WebDriver
             http://
seleniumhq.org/projects/webdriver/
WebDriver
•   Successor to the Selenium project.
•   Also known as “Selenium 2”.
•   Sponsored and driven by Google.
•   Becoming a W3C standard.
    – http://dvcs.w3.org/hg/webdriver/raw-
      file/515b648d58ff/webdriver-spec.html
Cross-browser
                           Automation
Java based, with many language bindings.
  import org.openqa.selenium.WebDriver; 
  import org.openqa.selenium.WebElement; 
  import org.openqa.selenium.By; 
  import org.openqa.selenium.firefox.FirefoxDriver; 

  WebDriver driver = new FirefoxDriver(); 
  driver.get("http://google.com"); 
  WebElement heading = driver.findElement(By.tagName("h1")); 
Mobile Browsers
• Rapidly improving.
  – iPad
  – iPhone
  – Android
  – Blackberry
• Can use real devices or emulators in most
  cases.
• A headless webkit based driver
  (PhantomJS) is in progress.
WebDriver API
• Geb sits on top of WebDriver so you very
  rarely deal with its API, though it's
  accessible if you need it.

• Geb never talks to the actual browser
  because that's what WebDriver does.
Driver dependency
• You need to pull in a specific driver
  implementation for each browser you want
  to work with.
  –   <dependency>
  –   <groupId>org.seleniumhq.selenium</groupId>
  –   <artifactId>selenium-firefox-driver</artifactId>
  –   <version>2.24.1</version>
  –   </dependency>
jQuery
http://jquery.com/
JQuery
• jQuery provides an incredibly powerful API
  for navigating and selecting content.
  – $("div#footer").prev().children();

  – CSS based, a whole lot better than XPath.
Geb's inspiration
• Geb features a “Navigator API” that is
  inspired by jQuery.

  – // This is Geb code, not jQuery JavaScript… $
    ("h1").previous().children();


• API is not identical.
Groovy
http://groovy-lang.org
Dynamic JVM Lang.
• Groovy is…
  – Compiled, never interpreted
  – Dynamic, optionally typed
  – 99% Java syntax compatible
  – Concise, clear and pragmatic
  – Great for DSLs
  – A comfortable Java alternative for most
Geb & Groovy
• Geb uses Groovy's dynamism to
  remove boilerplate.
 import geb.*
 Browser.drive {
      to GoogleHomePage
      at GoogleHomePage
      search.forTerm "wikipedia“
      at GoogleResultsPage 
      assert firstResultLink.text() == "Wikipedia" 
      firstResultLink.click()
      waitFor { at WikipediaPage }
      } 
Page Objects
 The key to not pulling your hair
out when dealing with web tests.
What are they?
•   In a phrase: Domain Modelling.

•   By modelling and creating abstractions, we can isolate implementation
    detail.

     $("input[name=username]").value("user")
     $("input[name=pwd]").value("password")
     $("input[type=submit]").click() 

•   Is far more fragile than this…

     void login(String username, String password) { 
            $("input[name=username]").value(username)
            $("input[name=pwd]").value(password)
            $("input[type=submit]").click()
     }

     login("user", "password") 
Just good programming
• It's the application of trusted principles;
  encapsulation and reuse.
• Not new at all, but new to the world of web
  testing/automation.
• Not just about modelling “pages”. It's
  about modelling all kinds of things in the
  domain of a user's actions online.
• Just giving symbolic names to page
  content is a great start.
Browser has-a Page
Browser.drive { 
     to GoogleHomePage 
     at GoogleHomePage 
     search.forTerm "wikipedia" 
     at GoogleResultsPage 
     assert firstResultLink.text() == "Wikipedia" 
     firstResultLink.click()
     waitFor { at WikipediaPage }
     } 


• The to() and click() methods are changing the
  underlying page.
• You can refer to the current page's content and
  methods just by name.
Geb's Page Objects
• Geb builds the Page Object pattern
  directly into the framework (though it is
  optional).
  import geb.*

  class GoogleHomePage extends Page { 
        static url = "http://google.com/ncr"  
        static at = { title == "Google" } 
        static content = { 
           search { module GoogleSearchModule }
        } 
  } 
Geb's Page Objects
• Features the “Content DSL” for naming
  content in a dynamic and powerful way.

  import geb.*
  class GoogleResultsPage extends Page { 
  static at = { waitFor { title.endsWith("Google Search") } }   
  static content = { 
          search { module GoogleSearchModule }  
          results { $("li.g") } 
          result { i -> results[i] }    
          resultLink { i -> result(i).find("a.l", 0) }
           firstResultLink { resultLink(0) }     } } 
Modules
• Modules are repeating and/or
  reappearing content.
 import geb.*
 class GoogleSearchModule extends Module {
     static content = {
         field { $("input", name: "q") } 
        button(to: GoogleResultsPage) { btnG() }
     }

    class StandardPage extends Page { 
         static content = {   gmod { module  GoogleSearchModule  }     } 
     } 
Testing
Geb's testing adapters
Geb for Testing
• Geb can be used with…
  –   Spock
  –   JUnit (3 & 4)
  –   TestNG
  –   EasyB
  –   Cucumber (Cuke4Duke)

• The majority of Geb users use Spock.
• Geb can dump HTML and screenshots for each
  “test” to help in debugging.
Navigator API
jQuery inspired content selection/
           navigation
The $() method
• Returns a Navigator object.
• General format:
  –$
   («css selector», «index/range», «attribute/text
    matchers»)


• Examples:
  – $("div") // all divs 
  – $("div", 0) // first div $("div", 0..2) // first three divs 
  – // The third section heading with text “Geb” $("h2", 2, id: "section", text: "Geb") 
CSS Selectors
• Full CSS3 if the target browser supports it.

  $("div.some-class p:first[title='something']")
  $("ul li a") $("table tr:nth-child(2n+1) td")
  $("div#content p:first-child::first-line") 



• CSS lookups are fast.
Attribute/Text match
• Can match on attribute values:
  – //<div foo="bar">
  – $("div", foo: "bar")

• The “text” attribute is special:
  – //<div>foo</div> 
  – $("div", text: "foo")

• Can use Regular Expressions:
  – //<div>foo</div> 
  – $("div", text: ~/f.+/) 
Predicates
• Geb supplies some handy predicates:

  $("p", text: startsWith("p")) 
  $("p", class: contains("section")) 
  $("p", id: endsWith(~/d/))
Relative Content
• $() returns a Navigator that allows you to find
  relative content.
   $("p").previous()
   $("p").prevAll()
   $("p").next()
   $("p").nextAll()
   $("p").parent()
   $("p").siblings()
   $("div").children() 

• Most of these methods take selectors, indexes
  and attribute text/matchers too.
  $("p").nextAll(".listing") 
Content DSL
Content DSL
class GoogleResultsPage extends Page {     
    static content = {
             results { $("li.g") }
             result { i -> results[i] }   
             resultLink { i -> result(i).find("a.l", 0) } 
             firstResultLink { resultLink(0) }     
     }
} 

• Content definitions can build upon each
  other.
• Content definitions are actually templates.
Optional Content
class OptionalPage extends Page {
    static content = { 
    errorMsg(required: false) { $("p.errorMsg") }
   }
 }

• By default, Geb will error if the content you
  select doesn't exist.
• The “required” option disables this check.
Dynamic Content
class DynamicPage extends Page {
     static content = { 
        errorMsg(wait: true) { $("p.errorMsg") }    
 }
}

• Geb will wait for some time for this content to
  appear.
• By default, it will look for it every 100ms for 5s
  before giving up. This is highly configurable.
• Same semantics as the waitFor {} method that
  can be used anywhere.
Expensive Content
class ExpensivePage extends Page { 
    static content = {  
       results(cache: true) { $("li.results") } 
       result { results[it] }    
 } 
} 


• By default, all content is transient.
• The cache option instructs Geb to hold on to the
  content, avoiding redundant lookups.
• Use carefully, can cause problems with dynamic
  pages.
Navigation
Getting around
The to() method
class GoogleHomePage extends Page { 
    static url = "http://google.com/ncr" 
}

• Pages can define a url that defines the
  page location.
• The to() method sends the browser there
  and sets that as the current page object.
• to GoogleHomePage The page url can be
  relative (will be resolved against a config
  driven base).
Content based navigation
class FrontPage { 
    static content = { 
    aboutUsLink(to: AboutUsPage) { $("div#nav ul li a", text: iStartsWith("About Us")) }    
      }
}

• When this content is clicked, the
  underlying page will be changed
  implicitly.
     to FrontPage
     aboutUsLink.click() 
     page instanceof AboutUsPage 
At Checking
• The “at checking” mechanism enables fail
  fast and less debugging.
class LoginPage extends Page { 
    static at = { $("h1").text() == "Please log in" }
 }

browser.at LoginPage 

• Will throw an exception if every statement
  of the at check is not true.
Driver Management
• Geb caches the WebDriver instance (per
  thread) and shares it across test cases.
• Manages clearing cookies and is
  configurable.
• This can be disabled and tuned.
Config. Management
• Looks for GebConfig class or GebConfig.groovy
  file (or class) on classpath.
  driver = { 
  new FirefoxDriver() 
  }

  waiting {
  timeout = 2 
  slow { timeout = 100 }
  } 

  reportsDir = "geb-reports“

  environments {
  chrome { driver = "chrome" }
  } 
What we didn't see
•   JavaScript interface
•   jQuery interface
•   Direct Downloading
•   Multi Window support
•   Frame support
•   Page Change Listening
•   Actions (e.g. Drag & Drop)
•   alert()/confirm() handling
Summary




JUnit3   Spock   Grails   JUnit4   EasyB
Thanks




         »       @gaurav_bansal
         »       gbansal@xebia.com /
           itsbansal@gmail.com
         »       +91-9899849992

Comprehensive Browser Automation Solution using Groovy, WebDriver & Obect Model

  • 1.
    Web Automation usingGroovy, WebDriver, JQuery and Page Object Model By: Gaurav Bansal
  • 2.
    Need… • We needa solution that is based on technology that is native to WebBrowsers. • We need a solution that requires no boilerplate code. • We need a solution that offers a powerful set of tools for matching a set of elements in a document. • We need a solution that offers in-built mechanism to incorporate Page-object model
  • 3.
  • 4.
    What is Geb? •Geb is a browser automation solution. • You can use it for… – Acceptance Testing Web Applications – Automating Web Sites – Screen Scraping • It brings together the… – Cross browser automation capabilities of WebDriver – Elegance of jQuery content selection – Expressiveness of the Groovy language – Robustness of Page Object modelling
  • 5.
    About the Project •Free Open Source, Apache License, Version 2.0. • Currently at version 0.7.0. – Home Page — http://www.gebish.org – The Book of Geb — http://www.gebish.org /manual/current – Source Code — https://github.com/geb/geb – User Mailing List — http:// xircles.codehaus.org/projects/geb/lists – In Maven Central — http:// mvnrepository.com/artifact/org.codehaus.geb
  • 6.
    Project Components • Theheart is the geb-core component which is all you really need (plus WebDriver). • For testing, you probably also want one of these as well: – geb-spock – geb-junit3 – geb-junit4 – geb-testng – geb-easyb • (Has been used from Cucumber as well). • There is also a Grails plugin.
  • 7.
    WebDriver http:// seleniumhq.org/projects/webdriver/
  • 8.
    WebDriver • Successor to the Selenium project. • Also known as “Selenium 2”. • Sponsored and driven by Google. • Becoming a W3C standard. – http://dvcs.w3.org/hg/webdriver/raw- file/515b648d58ff/webdriver-spec.html
  • 9.
    Cross-browser Automation Java based, with many language bindings. import org.openqa.selenium.WebDriver;  import org.openqa.selenium.WebElement;  import org.openqa.selenium.By;  import org.openqa.selenium.firefox.FirefoxDriver;  WebDriver driver = new FirefoxDriver();  driver.get("http://google.com");  WebElement heading = driver.findElement(By.tagName("h1")); 
  • 10.
    Mobile Browsers • Rapidlyimproving. – iPad – iPhone – Android – Blackberry • Can use real devices or emulators in most cases. • A headless webkit based driver (PhantomJS) is in progress.
  • 11.
    WebDriver API • Gebsits on top of WebDriver so you very rarely deal with its API, though it's accessible if you need it. • Geb never talks to the actual browser because that's what WebDriver does.
  • 12.
    Driver dependency • Youneed to pull in a specific driver implementation for each browser you want to work with. – <dependency> – <groupId>org.seleniumhq.selenium</groupId> – <artifactId>selenium-firefox-driver</artifactId> – <version>2.24.1</version> – </dependency>
  • 13.
  • 14.
    JQuery • jQuery providesan incredibly powerful API for navigating and selecting content. – $("div#footer").prev().children(); – CSS based, a whole lot better than XPath.
  • 15.
    Geb's inspiration • Gebfeatures a “Navigator API” that is inspired by jQuery. – // This is Geb code, not jQuery JavaScript… $ ("h1").previous().children(); • API is not identical.
  • 16.
  • 17.
    Dynamic JVM Lang. •Groovy is… – Compiled, never interpreted – Dynamic, optionally typed – 99% Java syntax compatible – Concise, clear and pragmatic – Great for DSLs – A comfortable Java alternative for most
  • 18.
    Geb & Groovy •Geb uses Groovy's dynamism to remove boilerplate. import geb.* Browser.drive { to GoogleHomePage at GoogleHomePage search.forTerm "wikipedia“ at GoogleResultsPage  assert firstResultLink.text() == "Wikipedia"  firstResultLink.click() waitFor { at WikipediaPage } } 
  • 19.
    Page Objects Thekey to not pulling your hair out when dealing with web tests.
  • 20.
    What are they? • In a phrase: Domain Modelling. • By modelling and creating abstractions, we can isolate implementation detail. $("input[name=username]").value("user") $("input[name=pwd]").value("password") $("input[type=submit]").click()  • Is far more fragile than this… void login(String username, String password) {  $("input[name=username]").value(username) $("input[name=pwd]").value(password) $("input[type=submit]").click() } login("user", "password") 
  • 21.
    Just good programming •It's the application of trusted principles; encapsulation and reuse. • Not new at all, but new to the world of web testing/automation. • Not just about modelling “pages”. It's about modelling all kinds of things in the domain of a user's actions online. • Just giving symbolic names to page content is a great start.
  • 22.
    Browser has-a Page Browser.drive {  to GoogleHomePage  at GoogleHomePage  search.forTerm "wikipedia"  at GoogleResultsPage  assert firstResultLink.text() == "Wikipedia"  firstResultLink.click() waitFor { at WikipediaPage } }  • The to() and click() methods are changing the underlying page. • You can refer to the current page's content and methods just by name.
  • 23.
    Geb's Page Objects •Geb builds the Page Object pattern directly into the framework (though it is optional). import geb.* class GoogleHomePage extends Page {  static url = "http://google.com/ncr"   static at = { title == "Google" }  static content = {  search { module GoogleSearchModule } }  } 
  • 24.
    Geb's Page Objects •Features the “Content DSL” for naming content in a dynamic and powerful way. import geb.* class GoogleResultsPage extends Page {  static at = { waitFor { title.endsWith("Google Search") } }    static content = {          search { module GoogleSearchModule }           results { $("li.g") }          result { i -> results[i] }             resultLink { i -> result(i).find("a.l", 0) }          firstResultLink { resultLink(0) }     } } 
  • 25.
    Modules • Modules arerepeating and/or reappearing content.  import geb.*  class GoogleSearchModule extends Module {      static content = {          field { $("input", name: "q") }          button(to: GoogleResultsPage) { btnG() }      }     class StandardPage extends Page {      static content = {   gmod { module  GoogleSearchModule  }     }  } 
  • 26.
  • 27.
    Geb for Testing •Geb can be used with… – Spock – JUnit (3 & 4) – TestNG – EasyB – Cucumber (Cuke4Duke) • The majority of Geb users use Spock. • Geb can dump HTML and screenshots for each “test” to help in debugging.
  • 28.
    Navigator API jQuery inspiredcontent selection/ navigation
  • 29.
    The $() method •Returns a Navigator object. • General format: –$ («css selector», «index/range», «attribute/text matchers») • Examples: – $("div") // all divs  – $("div", 0) // first div $("div", 0..2) // first three divs  – // The third section heading with text “Geb” $("h2", 2, id: "section", text: "Geb") 
  • 30.
    CSS Selectors • FullCSS3 if the target browser supports it. $("div.some-class p:first[title='something']") $("ul li a") $("table tr:nth-child(2n+1) td") $("div#content p:first-child::first-line")  • CSS lookups are fast.
  • 31.
    Attribute/Text match • Canmatch on attribute values: – //<div foo="bar"> – $("div", foo: "bar") • The “text” attribute is special: – //<div>foo</div>  – $("div", text: "foo") • Can use Regular Expressions: – //<div>foo</div>  – $("div", text: ~/f.+/) 
  • 32.
    Predicates • Geb suppliessome handy predicates: $("p", text: startsWith("p"))  $("p", class: contains("section"))  $("p", id: endsWith(~/d/))
  • 33.
    Relative Content • $()returns a Navigator that allows you to find relative content. $("p").previous() $("p").prevAll() $("p").next() $("p").nextAll() $("p").parent() $("p").siblings() $("div").children()  • Most of these methods take selectors, indexes and attribute text/matchers too. $("p").nextAll(".listing") 
  • 34.
  • 35.
    Content DSL class GoogleResultsPage extends Page {      static content = {         results { $("li.g") }         result { i -> results[i] }            resultLink { i -> result(i).find("a.l", 0) }          firstResultLink { resultLink(0) }      } }  • Content definitions can build upon each other. • Content definitions are actually templates.
  • 36.
    Optional Content class OptionalPage extends Page {     static content = {      errorMsg(required: false) { $("p.errorMsg") }    }  } • Bydefault, Geb will error if the content you select doesn't exist. • The “required” option disables this check.
  • 37.
    Dynamic Content class DynamicPage extends Page {      static content = {          errorMsg(wait: true) { $("p.errorMsg") }      } } • Gebwill wait for some time for this content to appear. • By default, it will look for it every 100ms for 5s before giving up. This is highly configurable. • Same semantics as the waitFor {} method that can be used anywhere.
  • 38.
    Expensive Content class ExpensivePage extends Page {      static content = {          results(cache: true) { $("li.results") }         result { results[it] }      }  }  • Bydefault, all content is transient. • The cache option instructs Geb to hold on to the content, avoiding redundant lookups. • Use carefully, can cause problems with dynamic pages.
  • 39.
  • 40.
    The to() method class GoogleHomePage extends Page {      static url = "http://google.com/ncr"  } •Pages can define a url that defines the page location. • The to() method sends the browser there and sets that as the current page object. • to GoogleHomePage The page url can be relative (will be resolved against a config driven base).
  • 41.
    Content based navigation class FrontPage {      static content = {      aboutUsLink(to: AboutUsPage) { $("div#nav ul li a", text: iStartsWith("About Us")) }     } } • When this content is clicked, the underlying page will be changed implicitly. to FrontPage aboutUsLink.click()  page instanceof AboutUsPage 
  • 42.
    At Checking • The“at checking” mechanism enables fail fast and less debugging. class LoginPage extends Page {      static at = { $("h1").text() == "Please log in" }  } browser.at LoginPage  • Will throw an exception if every statement of the at check is not true.
  • 43.
    Driver Management • Gebcaches the WebDriver instance (per thread) and shares it across test cases. • Manages clearing cookies and is configurable. • This can be disabled and tuned.
  • 44.
    Config. Management • Looksfor GebConfig class or GebConfig.groovy file (or class) on classpath. driver = {  new FirefoxDriver()  } waiting { timeout = 2  slow { timeout = 100 } }  reportsDir = "geb-reports“ environments { chrome { driver = "chrome" } } 
  • 45.
    What we didn'tsee • JavaScript interface • jQuery interface • Direct Downloading • Multi Window support • Frame support • Page Change Listening • Actions (e.g. Drag & Drop) • alert()/confirm() handling
  • 46.
    Summary JUnit3 Spock Grails JUnit4 EasyB
  • 47.
    Thanks » @gaurav_bansal » gbansal@xebia.com / itsbansal@gmail.com » +91-9899849992