# Browser Automation with Geb, Spock, and Groovy ## John Flinchbaugh ### john@hjsoft.com ### http://hjsoft.com/blog/
# Browser Automation is Like Magic
# Layers of Abstraction
## A Web App to Automate or Test
## Web Browser * Chrome * Firefox * Internet Explorer * Safari
## Third-Party Binary Driver * `chromedriver` * `geckodriver`
## WebDriver (Java) ``` 'org.seleniumhq.selenium:selenium-chrome-driver:3.14.0' 'org.seleniumhq.selenium:selenium-firefox-driver:3.14.0' 'org.seleniumhq.selenium:selenium-htmlunit-driver:3.14.0' ```
## Geb * Expressive API Written in Groovy * jQuery-like CSS3 Expressions ``` go 'https://mysearch.work.com/' $('form', name: 'q') = 'nicest api' $('form input', name: 'search').click() assert $('.results .result span.title')*.text() == [ 'Geb', 'Groovy', 'Spock', ] ```
## Spock * Expressive Test Framework Written in Groovy * Built in Mocking and Spying
## Gradle * Build System That's Not Maven
## Groovy * Looks Familiar Coming From Java * Dynamic Typing * Metaprogramming * Fun! * Still Just JVM Bytecode
# Let's Code
## Initialize a Gradle Project ``` % gradle init --type java-library --test-framework spock ```
## Add Geb and Webdriver `build.gradle` ``` dependencies { testImplementation 'org.seleniumhq.selenium:selenium-chrome-driver:3.14.0' testImplementation 'org.gebish:geb-spock:2.2' // testImplementation 'org.gebish:geb-core:2.2' } ```
## Geb Spec `src/test/PokerFunctionalSpec.groovy` ``` import geb.spock.* import geb.* import spock.lang.* class PokerFunctionalSpec extends GebReportingSpec { void 'it shows all the cards'() { given: 'we go to the poker app' go 'file:///Users/jflinchbaugh/my/poker-jquery/index.html' expect: $('.card')*.text() == [] } } ```
## Configure Geb `src/test/GebConfig.groovy` ``` reportsDir = new File('build/reports/geb') ```
## Output Test Failures `build.gradle` ``` test { testLogging { showStandardStreams = true events 'failed', 'passed' exceptionFormat 'full' } } ```
## Click the Cards `src/test/PokerFunctionalSpec.groovy` ``` void 'clicking each card shows it big'() { given: 'we go to the poker app' go url when: 'we click the card' $('.card', text: '1').click() then: 'see the big card' $('.show').text() == '1' } ```
## Slow It Down `src/test/PokerFunctionalSpec.groovy` ``` void cleanup() { sleep 2000 } ```
## Repeat and Unroll `src/test/PokerFunctionalSpec.groovy` ``` @Unroll void 'clicking the #estimate card shows it big'() { given: 'we go to the poker app' go url when: 'we click the card' $('.card', text: estimate).click() then: 'see the big card' $('.show').text() == estimate where: estimate << [ 1, 2, 3 ]*.toString() } ```
## Wait For Asynchronous Events `src/test/PokerFunctionalSpec.groovy` ``` waitFor { $('.show').text() } == estimate ```
## Page Model `src/test/GooglePage.groovy` ``` import geb.* class GooglePage extends Page { static at = { title == 'Google' } static url = 'https://www.google.com/' static content = { form { $('form') } submit { $('form input', name: 'btnK', 0) } } } ```
## Page Model `src/test/GoogleSearchPage.groovy` ``` import geb.* class GoogleSearchPage extends Page { static at = { title ==~ /.* Google Search/ } static content = { resultTitles { $('h3') } } } ```
## Google Spec `src/test/GoogleFunctionalSpec.groovy` ``` import geb.spock.* import geb.* import spock.lang.* class GoogleFunctionalSpec extends GebReportingSpec { void cleanup() { sleep 2000 } void 'google searches'() { given: 'we go to google' to GooglePage when: 'we submit a search' form.q = 'geb spock' waitFor { submit().click() } then: 'we land at the search page' at GoogleSearchPage and: 'there are results' resultTitles()*.text() and: 'every result mentions geb' resultTitles()*.text().collect { it ==~ /(?i).*geb.*/ }.every() } } ```
# The End (...or Beginning) ## John Flinchbaugh ### john@hjsoft.com ### http://hjsoft.com/blog/ ## Source Code and Slides ### https://github.com/jflinchbaugh/geb-preso ### https://jflinchbaugh.github.io/geb-preso/