testing web applications with geb
DESCRIPTION
As presented at No Fluff Just Stuff San Antonio, April 14th 2012.TRANSCRIPT
![Page 1: Testing Web Applications with GEB](https://reader034.vdocument.in/reader034/viewer/2022052504/554a1ab3b4c905825d8b524e/html5/thumbnails/1.jpg)
© 2012 Howard M. Lewis Ship
Testing Web Applications with GEBHoward M. Lewis ShipTWD [email protected]@hlship
![Page 2: Testing Web Applications with GEB](https://reader034.vdocument.in/reader034/viewer/2022052504/554a1ab3b4c905825d8b524e/html5/thumbnails/2.jpg)
Testing Web Applications
What's your process?
Manual?
Selenium?
What about Ajax?
![Page 3: Testing Web Applications with GEB](https://reader034.vdocument.in/reader034/viewer/2022052504/554a1ab3b4c905825d8b524e/html5/thumbnails/3.jpg)
Spoiled by jQuery
$(".order-summary .subtotal").text() == "107.95"
<div class="summary">
…
<div class="subtotal">107.95</div>
http://jquery.com/
![Page 4: Testing Web Applications with GEB](https://reader034.vdocument.in/reader034/viewer/2022052504/554a1ab3b4c905825d8b524e/html5/thumbnails/4.jpg)
Drives:
or limited HtmlUnit (browserless)
Selenium WebDriver
http://seleniumhq.org/
![Page 5: Testing Web Applications with GEB](https://reader034.vdocument.in/reader034/viewer/2022052504/554a1ab3b4c905825d8b524e/html5/thumbnails/5.jpg)
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");
![Page 6: Testing Web Applications with GEB](https://reader034.vdocument.in/reader034/viewer/2022052504/554a1ab3b4c905825d8b524e/html5/thumbnails/6.jpg)
driver.get("http://www.google.com");
WebElement element = driver.findElement(By.name("q"));
element.sendKeys("Cheese!");
element.submit();
new WebDriverWait(driver, 10)).until(new ExpectedCondition<Boolean>() { public Boolean apply(WebDriver d) { return d.getTitle().toLowerCase().startsWith("cheese!"); }};
Triggers Ajax UpdateTriggers Ajax Update
![Page 7: Testing Web Applications with GEB](https://reader034.vdocument.in/reader034/viewer/2022052504/554a1ab3b4c905825d8b524e/html5/thumbnails/7.jpg)
Java == High Ceremony
![Page 8: Testing Web Applications with GEB](https://reader034.vdocument.in/reader034/viewer/2022052504/554a1ab3b4c905825d8b524e/html5/thumbnails/8.jpg)
Groovy == Low Ceremony + Dynamic
![Page 9: Testing Web Applications with GEB](https://reader034.vdocument.in/reader034/viewer/2022052504/554a1ab3b4c905825d8b524e/html5/thumbnails/9.jpg)
Power of Selenium WebDriver 2.15.0
Elegance of jQuery content selection
Robustness of Page Object modelling
Expressiveness of Groovy
First Class Documentation
![Page 10: Testing Web Applications with GEB](https://reader034.vdocument.in/reader034/viewer/2022052504/554a1ab3b4c905825d8b524e/html5/thumbnails/10.jpg)
Running GEB Interactively
import groovy.grape.GrapeGrape.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.groovy
handlers=java.util.logging.ConsoleHandler
java.util.logging.ConsoleHandler.level=WARNINGjava.util.logging.ConsoleHandler.formatter=java.util.logging.SimpleFormatter
geb-logging.properties
![Page 11: Testing Web Applications with GEB](https://reader034.vdocument.in/reader034/viewer/2022052504/554a1ab3b4c905825d8b524e/html5/thumbnails/11.jpg)
GEB Interactive$ groovyshGroovy 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.*]===> nullgroovy:000> b = new Browser(baseUrl: "http://google.com/")===> geb.Browser@190a0d51groovy:000> b.go()===> nullgroovy:000>
![Page 12: Testing Web Applications with GEB](https://reader034.vdocument.in/reader034/viewer/2022052504/554a1ab3b4c905825d8b524e/html5/thumbnails/12.jpg)
Google Demo
![Page 13: Testing Web Applications with GEB](https://reader034.vdocument.in/reader034/viewer/2022052504/554a1ab3b4c905825d8b524e/html5/thumbnails/13.jpg)
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
![Page 14: Testing Web Applications with GEB](https://reader034.vdocument.in/reader034/viewer/2022052504/554a1ab3b4c905825d8b524e/html5/thumbnails/14.jpg)
IMDB Demo
![Page 15: Testing Web Applications with GEB](https://reader034.vdocument.in/reader034/viewer/2022052504/554a1ab3b4c905825d8b524e/html5/thumbnails/15.jpg)
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()
![Page 16: Testing Web Applications with GEB](https://reader034.vdocument.in/reader034/viewer/2022052504/554a1ab3b4c905825d8b524e/html5/thumbnails/16.jpg)
Browser
go() : voidquit() : voidpage : Page
Page
pageUrl : Stringtitle : String
$(…) : NavigatorwaitFor(…) : Object
startsWith(…) : TextMatchercontains(…) : TextMatcherendsWith(…) : TextMatcher
Navigator
x : inty : int
width : intheight : int
disabled : booleanempty : boolean
displayed : booleanadd(String) : Navigator
click() : voidfilter(...) : Navigatorfind(…) : Navigator
not(String) : Navigatorfirst() : Navigatorlast() : Navigator
getAt(…) : NavigatorgetAttribute(String) : String
has(String) : Navigatorparents(String) : Navigator
parentsUntil(String): Navigatorsize() : int
text() : Stringvalue() : Object
Delegation
GebSpec
Deleg
atio
n
![Page 17: Testing Web Applications with GEB](https://reader034.vdocument.in/reader034/viewer/2022052504/554a1ab3b4c905825d8b524e/html5/thumbnails/17.jpg)
$("p") ➠ all <p>
$("p", 3) ➠ 4th <p>
$("p")[3] ➠ 4th <p>
$("p")[0..2] ➠ 1st through 3rd <p>
$(css selector, index, attribute / text matchers)
CSS3
![Page 18: Testing Web Applications with GEB](https://reader034.vdocument.in/reader034/viewer/2022052504/554a1ab3b4c905825d8b524e/html5/thumbnails/18.jpg)
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
![Page 19: Testing Web Applications with GEB](https://reader034.vdocument.in/reader034/viewer/2022052504/554a1ab3b4c905825d8b524e/html5/thumbnails/19.jpg)
Attribute PredicatesCase Sensitive Case Insensitive Description
startsWith iStartsWith start with value
contains iContains contains the value anywhere
endsWith iEndsWith end with value
constainsWord iContainsWord contains value surrounded by whitespace (or at begin or end)
notStartsWith iNotStartsWith DOES NOT start with value
notContains iNotContains DOES NOT contain value anywhere
notEndsWith iNotEndsWith DOES NOT end with value
notContainsWord iNotContainsWord DOES NOT contain value (surrounded by whitespace, or at begin or end)
![Page 20: Testing Web Applications with GEB](https://reader034.vdocument.in/reader034/viewer/2022052504/554a1ab3b4c905825d8b524e/html5/thumbnails/20.jpg)
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
![Page 21: Testing Web Applications with GEB](https://reader034.vdocument.in/reader034/viewer/2022052504/554a1ab3b4c905825d8b524e/html5/thumbnails/21.jpg)
Navigators are Groovy Collections
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
each() is a Groovy Collection method
![Page 22: Testing Web Applications with GEB](https://reader034.vdocument.in/reader034/viewer/2022052504/554a1ab3b4c905825d8b524e/html5/thumbnails/22.jpg)
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
![Page 23: Testing Web Applications with GEB](https://reader034.vdocument.in/reader034/viewer/2022052504/554a1ab3b4c905825d8b524e/html5/thumbnails/23.jpg)
Pages and Modules
![Page 24: Testing Web Applications with GEB](https://reader034.vdocument.in/reader034/viewer/2022052504/554a1ab3b4c905825d8b524e/html5/thumbnails/24.jpg)
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()
![Page 25: Testing Web Applications with GEB](https://reader034.vdocument.in/reader034/viewer/2022052504/554a1ab3b4c905825d8b524e/html5/thumbnails/25.jpg)
Solution: Model Pages not just DOMBrowser
go() : voidquit() : voidpage : Page
at(…) : boolean
Page
pageUrl : Stringtitle : String
$(…) : NavigatorwaitFor(…) : ObjectverifyAt() : booleanbrowser: Browser
Delegation
class Home extends geb.Page { static url = "" static at = { title == "The Internet Movie Database (IMDb)" }}
groovy:000> b.$(".home").click(Home) ===> nullgroovy:000> b.verifyAt()===> truegroovy:000> b.at Home===> truegroovy:000> b.page===> Homegroovy:000> b.to Home===> null
![Page 26: Testing Web Applications with GEB](https://reader034.vdocument.in/reader034/viewer/2022052504/554a1ab3b4c905825d8b524e/html5/thumbnails/26.jpg)
Page Contentclass 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
![Page 27: Testing Web Applications with GEB](https://reader034.vdocument.in/reader034/viewer/2022052504/554a1ab3b4c905825d8b524e/html5/thumbnails/27.jpg)
to / do / at
groovy:000> b.to Home===> nullgroovy:000> b.q = "Forbidden Planet"===> Forbidden Planetgroovy:000> b.searchForm.go.click()===> nullgroovy:000> b.at Search===> truegroovy:000>
![Page 28: Testing Web Applications with GEB](https://reader034.vdocument.in/reader034/viewer/2022052504/554a1ab3b4c905825d8b524e/html5/thumbnails/28.jpg)
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") } }
![Page 29: Testing Web Applications with GEB](https://reader034.vdocument.in/reader034/viewer/2022052504/554a1ab3b4c905825d8b524e/html5/thumbnails/29.jpg)
Content OptionsOption Type Default Description
cache boolean false Evaluate content once, or on each access
required boolean true
Error on page load if content does not exist (use false for optional or Ajax-loaded)
to Page or Class, list of Page or Class null On a link, identify the page
the link submits to
wait varies null Wait for content to become available (via Ajax/DHTML)
![Page 30: Testing Web Applications with GEB](https://reader034.vdocument.in/reader034/viewer/2022052504/554a1ab3b4c905825d8b524e/html5/thumbnails/30.jpg)
Page Methodsclass 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===> nullgroovy:000> b.clickBoxOffice 0===> truegroovy:000>
![Page 31: Testing Web Applications with GEB](https://reader034.vdocument.in/reader034/viewer/2022052504/554a1ab3b4c905825d8b524e/html5/thumbnails/31.jpg)
Problem: Re-used web pagesclass Movie extends Page {
static at = { assert title.startsWith("Blade Runner") true }
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)===> nullgroovy:000> b.page===> Moviegroovy:000> b.verifyAt()ERROR org.codehaus.groovy.runtime.powerassert.PowerAssertionError:assert title.startsWith("Blade Runner") | | | false The Bugs Bunny/Road-Runner Movie (1979) - IMDb
GEB 0.7 will magically convert to asserts
![Page 32: Testing Web Applications with GEB](https://reader034.vdocument.in/reader034/viewer/2022052504/554a1ab3b4c905825d8b524e/html5/thumbnails/32.jpg)
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() } }}
![Page 33: Testing Web Applications with GEB](https://reader034.vdocument.in/reader034/viewer/2022052504/554a1ab3b4c905825d8b524e/html5/thumbnails/33.jpg)
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) }}
![Page 34: Testing Web Applications with GEB](https://reader034.vdocument.in/reader034/viewer/2022052504/554a1ab3b4c905825d8b524e/html5/thumbnails/34.jpg)
groovy:000> b.searchForm.go.click()===> nullgroovy: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) }
click()
![Page 35: Testing Web Applications with GEB](https://reader034.vdocument.in/reader034/viewer/2022052504/554a1ab3b4c905825d8b524e/html5/thumbnails/35.jpg)
Problem: Duplication on Pagesb.$("#navbar-query") << "Bladerunner"b.$("#navbar-submit-button").click()
Other examples:
Login / Logout / Register
"Contact Us" & other boilerplate
"Mark Favorite"
View Product Details
Bid / Buy
![Page 36: Testing Web Applications with GEB](https://reader034.vdocument.in/reader034/viewer/2022052504/554a1ab3b4c905825d8b524e/html5/thumbnails/36.jpg)
Solution: Modulesclass 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===> nullgroovy:000> b.searchForm.field << "Serenity"===> [org.openqa.selenium.firefox.FirefoxWebElement@1ef44b1f]groovy:000> b.searchForm.go.click()===> nullgroovy:000> b.page===> Search
![Page 37: Testing Web Applications with GEB](https://reader034.vdocument.in/reader034/viewer/2022052504/554a1ab3b4c905825d8b524e/html5/thumbnails/37.jpg)
Problem: Repeating Elements
<table class="cast_list"> <tr> <td class="primary_photo"> … <td class="name"> … <td class="ellipsis"> … <td class="character"> …
![Page 38: Testing Web Applications with GEB](https://reader034.vdocument.in/reader034/viewer/2022052504/554a1ab3b4c905825d8b524e/html5/thumbnails/38.jpg)
Solution: Module Listsclass CastRow extends Module { static content = { actorName { $("td.name").text() } characterName { $("td.character").text() } }} 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() } }}
Scope limited to each <tr>
![Page 39: Testing Web Applications with GEB](https://reader034.vdocument.in/reader034/viewer/2022052504/554a1ab3b4c905825d8b524e/html5/thumbnails/39.jpg)
groovy:000> b.at(new Movie(expectedTitle: "Blade Runner")) ===> truegroovy:000> b.castList[0].actorName===> Harrison Fordgroovy:000> b.castList[0].characterName===> Rick Deckardgroovy: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]
![Page 40: Testing Web Applications with GEB](https://reader034.vdocument.in/reader034/viewer/2022052504/554a1ab3b4c905825d8b524e/html5/thumbnails/40.jpg)
JavaScript and Ajax
![Page 41: Testing Web Applications with GEB](https://reader034.vdocument.in/reader034/viewer/2022052504/554a1ab3b4c905825d8b524e/html5/thumbnails/41.jpg)
js object
groovy:000> b = new Browser(baseUrl: "http://jquery.org")===> geb.Browser@5ec22978groovy:000> b.go()===> nullgroovy:000> b.js."document.title"===> jQuery Project
Access simple page properties
![Page 42: Testing Web Applications with GEB](https://reader034.vdocument.in/reader034/viewer/2022052504/554a1ab3b4c905825d8b524e/html5/thumbnails/42.jpg)
Executing JavaScript
groovy:000> b.js.exec '''groovy:001> $("img").css("background-color", "red").fadeOut()groovy:002> '''===> nullgroovy:000> b.js.exec 1, 2, "return arguments[0] + arguments[1];"===> 3
Text evaluated in-browser
![Page 43: Testing Web Applications with GEB](https://reader034.vdocument.in/reader034/viewer/2022052504/554a1ab3b4c905825d8b524e/html5/thumbnails/43.jpg)
jQuery Hook
groovy:000> b.$("img").jquery.fadeIn()===> [org.openqa.selenium.firefox.FirefoxWebElement@de86fd70, org.openqa.selenium.firefox.FirefoxWebElement@615e6612, 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]
Silently fails unless jQuery on page
More methodMissing() magic!
![Page 44: Testing Web Applications with GEB](https://reader034.vdocument.in/reader034/viewer/2022052504/554a1ab3b4c905825d8b524e/html5/thumbnails/44.jpg)
WaitingwaitFor { condition }waitFor timeout { condition }waitFor timeout, interval { condition }waitFor "preset" { condition }
![Page 45: Testing Web Applications with GEB](https://reader034.vdocument.in/reader034/viewer/2022052504/554a1ab3b4c905825d8b524e/html5/thumbnails/45.jpg)
Testing Framework Integration
![Page 46: Testing Web Applications with GEB](https://reader034.vdocument.in/reader034/viewer/2022052504/554a1ab3b4c905825d8b524e/html5/thumbnails/46.jpg)
Reportingpackage myapp.tests
import geb.spock.*
class Login extends GebReportingSpec {
def "successful login"() {
when: go "login" username = "user1" report "login screen" login().click()
then: title == "Welcome, User1"
}}
Capture HTML and screenshot
Base class that reports at end of each test
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
![Page 47: Testing Web Applications with GEB](https://reader034.vdocument.in/reader034/viewer/2022052504/554a1ab3b4c905825d8b524e/html5/thumbnails/47.jpg)
Base Classes
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 end of each test
Report failures only
![Page 48: Testing Web Applications with GEB](https://reader034.vdocument.in/reader034/viewer/2022052504/554a1ab3b4c905825d8b524e/html5/thumbnails/48.jpg)
Delegationpackage myapp.tests
import geb.spock.*
class Login extends GebReportingSpec {
def "successful login"() {
when: go "login" username = "user1" report "login screen" login().click()
then: title == "Welcome, User1" }}
Browser
Page
Geb[Reporting]Spec
Deleg
atio
nDe
legat
ion
![Page 49: Testing Web Applications with GEB](https://reader034.vdocument.in/reader034/viewer/2022052504/554a1ab3b4c905825d8b524e/html5/thumbnails/49.jpg)
Configuration
![Page 50: Testing Web Applications with GEB](https://reader034.vdocument.in/reader034/viewer/2022052504/554a1ab3b4c905825d8b524e/html5/thumbnails/50.jpg)
src/test/resources/GebConfig.groovy
GebConfig.groovyimport org.openqa.selenium.firefox.FirefoxDriverimport 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
![Page 51: Testing Web Applications with GEB](https://reader034.vdocument.in/reader034/viewer/2022052504/554a1ab3b4c905825d8b524e/html5/thumbnails/51.jpg)
Environment$ grade test -Dgeb.env=chrome
src/test/resources/GebConfig.groovyimport org.openqa.selenium.firefox.FirefoxDriverimport 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() } }}
![Page 52: Testing Web Applications with GEB](https://reader034.vdocument.in/reader034/viewer/2022052504/554a1ab3b4c905825d8b524e/html5/thumbnails/52.jpg)
Waiting Configuration
GebConfig.groovywaiting { timeout = 10 retryInterval = 0.5
presets { slow { timeout = 20, retryInterval = 1 } quick { timeout = 1 } } }
![Page 53: Testing Web Applications with GEB](https://reader034.vdocument.in/reader034/viewer/2022052504/554a1ab3b4c905825d8b524e/html5/thumbnails/53.jpg)
More Info
![Page 54: Testing Web Applications with GEB](https://reader034.vdocument.in/reader034/viewer/2022052504/554a1ab3b4c905825d8b524e/html5/thumbnails/54.jpg)
http://www.gebish.org
![Page 55: Testing Web Applications with GEB](https://reader034.vdocument.in/reader034/viewer/2022052504/554a1ab3b4c905825d8b524e/html5/thumbnails/55.jpg)
https://github.com/geb/geb
![Page 56: Testing Web Applications with GEB](https://reader034.vdocument.in/reader034/viewer/2022052504/554a1ab3b4c905825d8b524e/html5/thumbnails/56.jpg)
http://howardlewisship.com
![Page 57: Testing Web Applications with GEB](https://reader034.vdocument.in/reader034/viewer/2022052504/554a1ab3b4c905825d8b524e/html5/thumbnails/57.jpg)
Q & A