• Share
  • Email
  • Embed
  • Like
  • Save
  • Private Content
Testing Web Applications with GEB
 

Testing Web Applications with GEB

on

  • 3,646 views

As presented at No Fluff Just Stuff San Antonio, April 14th 2012.

As presented at No Fluff Just Stuff San Antonio, April 14th 2012.

Statistics

Views

Total Views
3,646
Views on SlideShare
2,996
Embed Views
650

Actions

Likes
3
Downloads
49
Comments
0

2 Embeds 650

http://d.hatena.ne.jp 648
https://www.google.co.jp 2

Accessibility

Upload Details

Uploaded via as Adobe PDF

Usage Rights

CC Attribution-NoDerivs LicenseCC Attribution-NoDerivs License

Report content

Flagged as inappropriate Flag as inappropriate
Flag as inappropriate

Select your reason for flagging this presentation as inappropriate.

Cancel
  • Full Name Full Name Comment goes here.
    Are you sure you want to
    Your message goes here
    Processing…
Post Comment
Edit your comment

    Testing Web Applications with GEB Testing Web Applications with GEB Presentation Transcript

    • Testing Web Applicationswith GEBHoward M. Lewis ShipTWD Consultinghlship@gmail.com@hlship © 2012 Howard M. Lewis Ship
    • Testing Web Applications Whats 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 Updatenew 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.0Elegance of jQuery content selectionRobustness of Page Object modellingExpressiveness of GroovyFirst Class Documentation
    • Running GEB Interactivelygeb.groovyimport 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-logging.propertieshandlers=java.util.logging.ConsoleHandlerjava.util.logging.ConsoleHandler.level=WARNINGjava.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 : intquit() : void title : String y : intpage : 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 DescriptionstartsWith iStartsWith start with valuecontains iContains contains the value anywhereendsWith iEndsWith end with value contains value surrounded by whitespace (or atconstainsWord iContainsWord begin or end)notStartsWith iNotStartsWith DOES NOT start with valuenotContains iNotContains DOES NOT contain value anywherenotEndsWith iNotEndsWith DOES NOT end with value DOES NOT contain value (surrounded bynotContainsWord 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 methodgroovy: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, WilliamSanderson=J.F. Sebastian, Brion James=Leon Kowalski, Joe Turkel=Dr. EldonTyrell, 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 Optionsstatic 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, orcache boolean false on each access Error on page load if content does not exist (userequired boolean true false for optional or Ajax- loaded) Page or Class, On a link, identify the pageto null list of Page or Class the link submits to Wait for content to becomewait varies null available (via Ajax/DHTML)
    • 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>
    • 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 ===> truedef 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 Listsclass 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"))===> 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]
    • 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 FrameworkIntegration
    • Reportingpackage myapp.tests Base class that reports at end of each testimport 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 testFramework Artifact Base Class Reporting Base ClassSpock geb-spock geb.spock.GebSpec geb.spock.GebReportingSpecJunit 4 geb-juni4 geb.junit4.GebTest geb.junit4.GebReportingTestJunit 3 geb-junit3 geb.junit3.GebTest geb.junit3.GebReportingTestTestNG geb-testng geb.testng.GebTest geb.testng.GebReportingTest Report failures only
    • Delegationpackage myapp.tests Pageimport geb.spock.* Delegationclass 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