Upgrade to Pro — share decks privately, control downloads, hide ads and more …

Test de aceptación con Geb - Commit Conf 2018

Sergio del Amo
December 19, 2018

Test de aceptación con Geb - Commit Conf 2018

En esta charla descubrirás Geb. Una solución de automatización del uso del navegador que te permitirá escribir test de aceptación que con facilidad y que te permitirán dormir bien por las noches.

Durante la charla realizaremos tests de la web Commit Conf 2018 https://2018.commit-conf.com

De este modo veras las características de Geb aplicadas a un ejemplo real.

Trataremos los siguientes conceptos:

• Gestión de las instancias de WebDriver. Como ejecutar nuestros tests en Chrome, Firefox o headless. • Configuración Built-in • Uso del Page Object Pattern para obtener unos tests de aceptación fáciles de mantener. • Navegación y selección de contenido • Integración con diferentes framework de test

Audiencia: Aunque Geb es una capa de Groovy encima de WebDriver no necesitas conocimientos específicos de Groovy. Cualquier desarrollador, es especial aquellos con conocimientos Java o lenguajes de JVM podrá seguir la charla sin problemas.

Sergio del Amo

December 19, 2018


  1. · Micronaut / Grails OCI Team · Gudalajara, Spain ·

    Curator of Groovycalamari.com · @sdelamo · http://sergiodelamo.es · greachconf.com organizer
  2. What is Geb? · Cross browser automation capabilities of WebDriver

    · A Groovy layer on top of WebDriver · WebDriver instance management
  3. What is Geb? · jQuery-like Navigator API and elegance of

    jQuery content selection · Robustness of Page Object modelling · Integrates with various test frameworks · Built-in configuration mechanism
  4. About the Project Free Open Source, Apache License, Version 2.0.

    · Home Page http://www.gebish.org · The Book of Geb · Source Code · User Mailing List · Geb in Maven Central
  5. For testing, you probably also want one of these as

    well: · geb-spock · geb-junit3 · geb-junit4 · geb-testng · geb-easyb
  6. 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. That's what WebDriver does.
  7. 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.20.0</version> </dependency>
  8. Driver management WebDriver Binaries Driver Gradle Plugin A plugin that

    downloads and caches WebDriver binaries specific to the OS the build runs on.
  9. jQuery http://jquery.com/ jQuery - write more, do less jQuery provides

    an incredibly powerful API for navigating and selecting content. $("div#footer").prev().childen(); CSS based, a whole lot better than XPath.
  10. Geb's inspiration Geb features a Navigator API that it inspired

    by jQuery. // This is Geb code, not jQuery JavaScript… $("h1").previous().children(); API is not identical.
  11. Groovy · Compiled, never interpreted · Dynamic, optionally typed ·

    99% Java syntax compatible · Concise, clear and pragmattic · Great for DSLs · A comfortable Java alternative for most
  12. Required Apache Groovy Knowledge http://groovy-lang.org You don't need to be

    a Groovy ninja to use Geb. Groovy knowledge can definitely help when things go wrong though.
  13. 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 } }
  14. Page Objects The key to not pulling your hair out

    when dealing with web tests. In a phrase: Domain Modelling. By modelling and creating abstractions, we can isolate implementation detail.
  15. Page Objects Just Good Programming Geb builds the Page Object

    pattern directly into the framework (though it is optional).
  16. Geb Pages The Page Object Pattern allows us to apply

    the same principles of modularity, reuse and encapsulation that we use in other aspects of programming to avoid such issues in browser automation code
  17. Browser has-a Page The to() and click() methods are changing

    the underlying page. You can refer to the current page's content and methods just by name.
  18. 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 { $("#sb").module(GoogleSearchModule) } results { $("li.g") } result { i -> results[i] } resultLink { i -> result(i).find("a.l", 0) } firstResultLink { resultLink(0) } } }
  19. Geb's Page Objects Very lightweight, minimum requirements are low. import

    geb.* class WikipediaPage extends Page { static at = { title == "Wikipedia" } }
  20. Modules Modules are repeating and/or reappearing content. <table id="book-results"> <thead>

    <tr> <th>Title</th><th>Author</th> </tr> </thead> <tbody> <tr> <td>Zero History</td> <td>William Gibson</td> </tr> <tr> <td>The Evolutionary Void</td> <td>Peter F. Hamilton</td> </tr> </tbody> </table>
  21. Modules Modules have a base, from which all content lookups

    are relative. class BooksPage extends Page { static content = { bookResults { $("table#book-results tbody tr", it).module(BookRow) } } } class BookRow extends Module { static content = { cell { $("td", it) } title { cell(0).text() } author { cell(1).text() } } }
  22. Modules We now have a model for a row in

    our table. expect: bookResults(0).title == "Zero History" bookResults(1).author == "Peter F. Hamilton Can be used for any reused/repeating content. Note: talking about domain objects, not HTML tables and rows.
  23. class Pagination extends Module { static content = { links(required:

    false) { $('a') } currentPage(required: false) { $('.currentStep')?.text()?.toInteger() ?: 1 } nextLink(required: false) { links.filter('.nextLink') } previousLink(required: false) { links.filter('.prevLink') } } void toPage(int pageNumber) { def link = links.filter(text: "$pageNumber") if (!link) { def exceptionMsg = "Page number $pageNumber not present in pagination" throw new IllegalArgumentException(exceptionMsg) } link.click() } boolean isLastPage() { nextLink.empty } void nextPage() { toPage(currentPage + 1) } void previousPage() { toPage(currentPage + -1) } boolean isFirstPage() { previousLink.empty} }
  24. Inheritance Pages (and modules) can be arranged in inheritance hierarchies.

    class Footer extends Module { static content = { copyright { $("p.copyright") } } } class StandardPage extends Page { static content = { footer { $('footer').module(Footer) } } } class FrontPage extends StandardPage {}
  25. Testing Geb's testing adapters. Geb can be used with… ·

    Spock · JUnit (3 & 4) · TestNG · EasyB · Cucumber (Cuke4Duke)
  26. Screenshots Geb can dump HTML and screenshots for each “test”

    to help in debugging. If using Spock, extend GebReportingSpec instead GebSpec. report()
  27. Navigator API jQuery inspired content selection/navigation The $() method Returns

    a Navigator object. General format: $(«css selector», «index/range», «attribute/text matchers»)
  28. 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")
  29. CSS Selector 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.
  30. Predicates Geb supplies some handy predicates: $("p", text: startsWith("p")) $("p",

    class: contains("section")) $("p", id: endsWith(~/\d/)) There are more of these.
  31. 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()
  32. Relative Content Most of these methods take selectors, indexes and

    attribute text/ matchers too. $("p").nextAll(".listing")
  33. Content DSL Content definitions can build upon each other. Content

    definitions are actually templates. class GoogleResultsPage extends Page { static content = { results { $("li.g") } result { i -> results[i] } resultLink { i -> result(i).find("a.l", 0) } firstResultLink { resultLink(0) } } }
  34. Optional Content By default, Geb will error if the content

    you select doesn't exist. The “required” option disables this check. class OptionalPage extends Page { static content = { errorMsg(required: false) { $("p.errorMsg") } } }
  35. Dynamic Content Geb will wait for some time for this

    content to appear. Same semantics as the waitFor {} method that can be used anywhere. class DynamicPage extends Page { static content = { errorMsg(wait: true) { $("p.errorMsg") } } }
  36. Navigation Getting around. The to() method class CommitConfHomePage extends Page

    { static url = "https://2018.commit-conf.com/" } Pages can define a url that defines the page location.
  37. Navigation The to() method sends the browser there and sets

    that as the current page object. to CommitConfHomePage
  38. Navigation The page url can be relative (will be resolved

    against a config driven base). browser.baseUrl = "https://2018.commit-conf.com" class CommitConfHomePage extends Page { static url = "/" } to CommitConfHomePage
  39. Dynamic URL url = baseUrl + url + covertToPath browser.baseUrl

    = 'http://localhost:8080' browser.to EditRoomPage, 1
  40. Content based navigation class FrontPage { static content = {

    aboutUsLink(to: AboutUsPage) { $("div#nav ul li a", text: iStartsWith("About Us")) } } } to FrontPage aboutUsLink.click() page instanceof AboutUsPage When this content is clicked, the underlying page will be changed implicitly.
  41. Geb Environments ./gradlew -Dgeb.env=chromeHeadless test // src/main/test/resources/GebConfig.groovy environments { chrome

    { driver = { new ChromeDriver() } } chromeHeadless { driver = { ChromeOptions o = new ChromeOptions() o.addArguments('headless') new ChromeDriver(o) } } …
  42. · Page Change Listening · Actions (e.g. Drag & Drop)

    · Caching expensive content lookups · alert()/confirm() handling · Scripting style (sans Page Objects) · Configuration & Environments