· Micronaut / Grails OCI Team
· Gudalajara, Spain
· Curator of Groovycalamari.com
· @sdelamo
· http://sergiodelamo.es
· greachconf.com organizer
Slide 3
Slide 3 text
http://groovycalamari.com
Slide 4
Slide 4 text
http://greachconf.com
Slide 5
Slide 5 text
Browser Automation Solution
· Acceptance Testing Web Applications
· Automating Web Sites
· Screen Scraping
Slide 6
Slide 6 text
What is Geb?
· Cross browser automation capabilities of WebDriver
· A Groovy layer on top of WebDriver
· WebDriver instance management
Slide 7
Slide 7 text
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
Slide 8
Slide 8 text
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
Slide 9
Slide 9 text
Marcin Erdmann
Geb project Lead
http://blog.proxerd.pl
Slide 10
Slide 10 text
Project Components
The heart is the geb-core component which is all you really need
(plus WebDriver).
Slide 11
Slide 11 text
For testing, you probably also want one of these as well:
· geb-spock
· geb-junit3
· geb-junit4
· geb-testng
· geb-easyb
Slide 12
Slide 12 text
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.
Slide 13
Slide 13 text
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
Slide 14
Slide 14 text
Driver management
WebDriver Binaries Driver Gradle Plugin
A plugin that downloads and caches WebDriver binaries specific to
the OS the build runs on.
Slide 15
Slide 15 text
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.
Slide 16
Slide 16 text
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.
Slide 17
Slide 17 text
Groovy
· Compiled, never interpreted
· Dynamic, optionally typed
· 99% Java syntax compatible
· Concise, clear and pragmattic
· Great for DSLs
· A comfortable Java alternative for most
Slide 18
Slide 18 text
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.
Slide 19
Slide 19 text
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 }
}
Slide 20
Slide 20 text
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.
Slide 21
Slide 21 text
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")
Slide 22
Slide 22 text
Page Objects
Just Good Programming
Geb builds the Page Object pattern directly into the framework
(though it is optional).
Slide 23
Slide 23 text
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
Slide 24
Slide 24 text
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.
Slide 25
Slide 25 text
No content
Slide 26
Slide 26 text
No content
Slide 27
Slide 27 text
No content
Slide 28
Slide 28 text
No content
Slide 29
Slide 29 text
No content
Slide 30
Slide 30 text
No content
Slide 31
Slide 31 text
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) }
}
}
Slide 32
Slide 32 text
Geb's Page Objects
Very lightweight, minimum requirements are low.
import geb.*
class WikipediaPage extends Page {
static at = { title == "Wikipedia" }
}
Slide 33
Slide 33 text
Modules
Slide 34
Slide 34 text
No content
Slide 35
Slide 35 text
No content
Slide 36
Slide 36 text
Modules
Modules are repeating and/or reappearing content.
TitleAuthor
Zero History
William Gibson
The Evolutionary Void
Peter F. Hamilton
Slide 37
Slide 37 text
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() }
}
}
Slide 38
Slide 38 text
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.
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 {}
Slide 43
Slide 43 text
Testing
Geb's testing adapters. Geb can be used with…
· Spock
· JUnit (3 & 4)
· TestNG
· EasyB
· Cucumber (Cuke4Duke)
Slide 44
Slide 44 text
Screenshots
Geb can dump HTML and screenshots for each “test” to help in
debugging.
If using Spock, extend GebReportingSpec instead GebSpec.
report()
Slide 45
Slide 45 text
Navigator API
jQuery inspired content selection/navigation
The $() method
Returns a Navigator object.
General format:
$(«css selector», «index/range», «attribute/text matchers»)
Slide 46
Slide 46 text
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")
Slide 47
Slide 47 text
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.
Slide 48
Slide 48 text
Attribute/Text matching
Can match on attribute values:
//
$("div", foo: "bar")
Slide 49
Slide 49 text
Attribute/Text matching
The “text” attribute is special:
//
foo
$("div", text: "foo")
Slide 50
Slide 50 text
Attribute/Text matching
Can use Regular Expressions:
//
foo
$("div", text: ~/f.+/)
Slide 51
Slide 51 text
Predicates
Geb supplies some handy predicates:
$("p", text: startsWith("p"))
$("p", class: contains("section"))
$("p", id: endsWith(~/\d/))
There are more of these.
Slide 52
Slide 52 text
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()
Slide 53
Slide 53 text
Relative Content
Most of these methods take selectors, indexes and attribute text/
matchers too.
$("p").nextAll(".listing")
Slide 54
Slide 54 text
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) }
}
}
Slide 55
Slide 55 text
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") }
}
}
Slide 56
Slide 56 text
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") }
}
}
Slide 57
Slide 57 text
Navigation go method
Getting around. The go() method
browser.go "https://2018.commit-conf.com/"
Slide 58
Slide 58 text
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.
Slide 59
Slide 59 text
Navigation
The to() method sends the browser there and sets that as the
current page object.
to CommitConfHomePage
Slide 60
Slide 60 text
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
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.
Slide 63
Slide 63 text
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)
}
}
…
Slide 64
Slide 64 text
· JavaScript interface
· jQuery interface
· Direct Downloading
· Multi Window support
· Frame support