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
110

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
Tweet

Transcript

  1. · Micronaut / Grails OCI Team
    · Gudalajara, Spain
    · Curator of Groovycalamari.com
    · @sdelamo
    · http://sergiodelamo.es
    · greachconf.com organizer

    View full-size slide

  2. http://groovycalamari.com

    View full-size slide

  3. http://greachconf.com

    View full-size slide

  4. Browser Automation Solution
    · Acceptance Testing Web Applications
    · Automating Web Sites
    · Screen Scraping

    View full-size slide

  5. What is Geb?
    · Cross browser automation capabilities of WebDriver
    · A Groovy layer on top of WebDriver
    · WebDriver instance management

    View full-size slide

  6. 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

    View full-size slide

  7. 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

    View full-size slide

  8. Marcin Erdmann
    Geb project Lead
    http://blog.proxerd.pl

    View full-size slide

  9. Project Components
    The heart is the geb-core component which is all you really need
    (plus WebDriver).

    View full-size slide

  10. For testing, you probably also want one of these as well:
    · geb-spock
    · geb-junit3
    · geb-junit4
    · geb-testng
    · geb-easyb

    View full-size slide

  11. 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.

    View full-size slide

  12. Driver dependency
    You need to pull in a specific driver implementation for each
    browser you want to work with.

    org.seleniumhq.selenium
    selenium-firefox-driver
    2.20.0

    View full-size slide

  13. Driver management
    WebDriver Binaries Driver Gradle Plugin
    A plugin that downloads and caches WebDriver binaries specific to
    the OS the build runs on.

    View full-size slide

  14. 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.

    View full-size slide

  15. 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.

    View full-size slide

  16. Groovy
    · Compiled, never interpreted
    · Dynamic, optionally typed
    · 99% Java syntax compatible
    · Concise, clear and pragmattic
    · Great for DSLs
    · A comfortable Java alternative for most

    View full-size slide

  17. 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.

    View full-size slide

  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 }
    }

    View full-size slide

  19. 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.

    View full-size slide

  20. Page Objects
    $("input[name=username]").value("user")
    $("input[name=password]").value("password")
    $("input[type=submit]").click()
    Is far more fragile than this…
    loginPage.login("user", "password")

    View full-size slide

  21. Page Objects
    Just Good Programming
    Geb builds the Page Object pattern directly into the framework
    (though it is optional).

    View full-size slide

  22. 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

    View full-size slide

  23. 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.

    View full-size slide

  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 { $("#sb").module(GoogleSearchModule) }
    results { $("li.g") }
    result { i -> results[i] }
    resultLink { i -> result(i).find("a.l", 0) }
    firstResultLink { resultLink(0) }
    }
    }

    View full-size slide

  25. Geb's Page Objects
    Very lightweight, minimum requirements are low.
    import geb.*
    class WikipediaPage extends Page {
    static at = { title == "Wikipedia" }
    }

    View full-size slide

  26. Modules
    Modules are repeating and/or reappearing content.



    TitleAuthor




    Zero History
    William Gibson


    The Evolutionary Void
    Peter F. Hamilton



    View full-size slide

  27. 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() }
    }
    }

    View full-size slide

  28. 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.

    View full-size slide

  29. Pagination Example

    View full-size slide

  30. Pagination Example
    static content = {
    pagination { $('ul.pagination').module(Pagination) }
    }

    View full-size slide

  31. 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}
    }

    View full-size slide

  32. 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 {}

    View full-size slide

  33. Testing
    Geb's testing adapters. Geb can be used with…
    · Spock
    · JUnit (3 & 4)
    · TestNG
    · EasyB
    · Cucumber (Cuke4Duke)

    View full-size slide

  34. Screenshots
    Geb can dump HTML and screenshots for each “test” to help in
    debugging.
    If using Spock, extend GebReportingSpec instead GebSpec.
    report()

    View full-size slide

  35. Navigator API
    jQuery inspired content selection/navigation
    The $() method
    Returns a Navigator object.
    General format:
    $(«css selector», «index/range», «attribute/text matchers»)

    View full-size slide

  36. 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")

    View full-size slide

  37. 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.

    View full-size slide

  38. Attribute/Text matching
    Can match on attribute values:
    //
    $("div", foo: "bar")

    View full-size slide

  39. Attribute/Text matching
    The “text” attribute is special:
    //foo
    $("div", text: "foo")

    View full-size slide

  40. Attribute/Text matching
    Can use Regular Expressions:
    //foo
    $("div", text: ~/f.+/)

    View full-size slide

  41. Predicates
    Geb supplies some handy predicates:
    $("p", text: startsWith("p"))
    $("p", class: contains("section"))
    $("p", id: endsWith(~/\d/))
    There are more of these.

    View full-size slide

  42. 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()

    View full-size slide

  43. Relative Content
    Most of these methods take selectors, indexes and attribute text/
    matchers too.
    $("p").nextAll(".listing")

    View full-size slide

  44. 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) }
    }
    }

    View full-size slide

  45. 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") }
    }
    }

    View full-size slide

  46. 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") }
    }
    }

    View full-size slide

  47. Navigation go method
    Getting around. The go() method
    browser.go "https://2018.commit-conf.com/"

    View full-size slide

  48. 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.

    View full-size slide

  49. Navigation
    The to() method sends the browser there and sets that as the
    current page object.
    to CommitConfHomePage

    View full-size slide

  50. 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

    View full-size slide

  51. Dynamic URL
    url = baseUrl + url + covertToPath
    browser.baseUrl = 'http://localhost:8080'
    browser.to EditRoomPage, 1

    View full-size slide

  52. 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.

    View full-size slide

  53. 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)
    }
    }

    View full-size slide

  54. · JavaScript interface
    · jQuery interface
    · Direct Downloading
    · Multi Window support
    · Frame support

    View full-size slide

  55. · Page Change Listening
    · Actions (e.g. Drag & Drop)
    · Caching expensive content lookups
    · alert()/confirm() handling
    · Scripting style (sans Page Objects)
    · Configuration & Environments

    View full-size slide