Slide 1

Slide 1 text

No content

Slide 2

Slide 2 text

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

Slide 39

Slide 39 text

Pagination Example

Slide 40

Slide 40 text

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

Slide 41

Slide 41 text

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

Slide 42

Slide 42 text

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

Slide 61

Slide 61 text

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

Slide 62

Slide 62 text

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

Slide 65

Slide 65 text

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

Slide 66

Slide 66 text

Q & A