This document provides an overview of using Geb to test Grails applications. It discusses:
- How Geb works by building on WebDriver to programmatically control browsers
- Structuring Geb tests using page objects and modules to organize test code
- Interacting with the application directly from Geb tests in Grails 2.x using the remote control plugin and in Grails 3.x by running in the same JVM
- Configuring the browser driver used for tests, including options for Firefox and Chrome
3. JACOB AAE MIKKELSEN
Senior Engineer at Lego
Microservice based architechture on JVM
Previously 4 years at Gennemtænkt IT
Consultant on Groovy and Grails
External Associate Professor - University of Southern
Denmark
@JacobAae
Blogs The Grails Diary
4. FUNCTIONAL TESTING
Ignores the specifics of the underlying software
component under test.
Merely asserts that providing certain input results in
certain output.
Web-application: Programmatically controlling a web
browser to simulate the actions of a user on a web page.
Traditionally been tedious, cumbersome and brittle
to do
Geb helps ease the pain
7. GEB HISTORY
Started in November 2009
Created by Luke Daley
Current project lead Marcin Erdman
8. WHY GEB
jQuery like selector syntax
Power of WebDriver (Easier api)
Robustness of Page Object modeling
Expressiveness of the Groovy language
Integrates well with build systems (Gradle/Maven)
Excellent user manual/documentation
9. GEB IMPLEMENTATION
Build on top of the WebDriver browser automation library
successor to the Selenium Remote Control (RC) testing
framework.
Selenium RC → JavaScript to interact
WebDriver → native browser drivers
Use JUnit or Spock
11. GEB SELECTORS (1)
Jquery like syntax
// match all 'p' elements on page
$("p")
// match the first 'p' element on the page
$("p", 0)
// All 'p' elements with a title value 'section'
$("div", title: "section")
// match the first 'p' element title attribute 'section'
$("p", 0, title: "section")
// match the first 'p' element with the class 'main'
$("p.main", 0)
12. GEB SELECTORS (2)
Selecting returns Navigatorobjects
// The parent of the first div
$("div", 0).parent()
// All tables with a cellspacing
// attribute value of 0 that are nested in a paragraph
$("p").find("table", cellspacing: '0')
16. MORE INTERACTION WITH FORMS
Consider the following HTML…
<form>
<input type="text" name="geb" value="Functional" />
</form>
The value can be read and written via property notation…
$("form").geb == "Functional"
$("form").geb = "Testing"
$("form").geb == "Testing"
These are literally shortcuts for…
$("form").find("input", name: "geb").value() == "Functional"
$("form").find("input", name: "geb").value("Testing")
$("form").find("input", name: "geb").value() == "Testing"
19. STRUCTURING GEB TESTS
Lets test a CRUD part of a grails application registrering
conference attendees
Lets test the following
1. Goto list of attendees page
2. Create new employee (incl. invalid data once)
3. Update the employee
4. Check data is updated
21. GEB SPEC (1)
The naive inmaintainable way!
void "Go to list page check initial state"() {
when:"The home page is visited"
go '/attendee/index'
then:
title == "Attendee List"
}
22. GEB SPEC (2)
The naive inmaintainable way!
void "Click new attendee button"() {
when:
$("a.create").click()
then:
title == "Create Attendee"
}
23. GEB SPEC (3)
The naive inmaintainable way!
void "Submit form with errors"() {
when:
$("button.btnprimary").click()
then:
title == "Create Attendee"
}
24. GEB SPEC (4)
The naive inmaintainable way!
void "Submit form with no errors"() {
when:
$('form').name = 'Bobby'
$('form').email = 'bobby@mail.dk'
and:
$("button.btnprimary").click()
then:
title == 'Show Attendee'
$('span.propertyvalue').find{ it.text() == 'Bobby'}
$('span.propertyvalue').find{ it.text() == 'bobby@mail.dk'}
}
27. GEB SPEC - THE BETTER WAY
If we make a few scenarios, there will be
Much duplication
Many places to correct if we change the layout / DOM
We can correct this using pages and modules
28. PAGE OBJECTS
Describes a web page
Url
How to check if we are at the correct place
Content we wish to interact with
33. MODULE FOR REPEATED CONTENT
IN A PAGE
import geb.Module
class AttendeeListItemModule extends Module {
static content = {
data { $("td", it) }
name { data(0).text() }
email { data(1).text() }
nationality { data(2).text() }
dateCreated { data(3).text() }
lastUpdated { data(4).text() }
}
}
34. MODULE FOR REPEATED CONTENT
IN A PAGE
AttendeeListPage.groovy
static content = {
menubar { module NavigationBarModule }
attendees { moduleList AttendeeListItemModule,
$("table tr").tail() }
}
35. MODULE FOR REPEATED CONTENT
IN A PAGE
when:
to AttendeeListPage
then:
attendees*.name.contains('Guillaume Laforge')
36. GEB SPEC - STRUCTURED (1)
Lets try to restructure the ugly spec from before
void "Go to list page check initial state"() {
when:
to AttendeeIndexPage
then:
at AttendeeIndexPage
}
37. GEB SPEC - STRUCTURED (2)
void "Click new attendee button"() {
when:
menubar.newAttendee.click()
then:
at AttendeeCreatePage
}
38. GEB SPEC - STRUCTURED (3)
void "Submit form with errors"() {
when:
submitButton.click()
then:
at AttendeeCreatePage
}
39. GEB SPEC - STRUCTURED (4)
void "Submit form with no errors"() {
when:
form.name = 'Bob'
form.email = 'bob@somemail.com'
and:
submitButton.click()
then:
at AttendeeShowPage
name == 'Bob'
email == 'bob@somemail.com'
}
43. GEB AND GRAILS 2.X
Must install plugin in BuildConfig.groovy
dependencies {
...
test("org.seleniumhq.selenium:seleniumsupport:2.45.0")
test("org.seleniumhq.selenium:seleniumfirefoxdriver:2.45.0")
test "org.gebish:gebspock:0.10.0"
}
plugins {
...
test "org.grails.plugins:geb:0.10.0"
}
44. GEB TESTS IN GRAILS 2.X
Tests placed in test/functionalfolder
Running the tests
grails testapp functional:
45. GEB AND GRAILS 3
Geb is default in build.gradle
dependencies {
...
testCompile "org.grails.plugins:geb"
// Note: It is recommended to update to a more robust driver
// (Chrome, Firefox etc.)
testRuntime 'org.seleniumhq.selenium:seleniumhtmlunitdriver:2.44.0
}
46. GEB TESTS IN GRAILS 3
Creating Geb Spec
grails createfunctionaltest MyGebScenario
Placing the test in src/integration-test/groovy
Running the tests
grails testapp integration
48. INTERACTING WITH APPLICATION
When some functionality is needed that is not exposed
through the browser, it can be necessary to interact with the
application under test.
49. GRAILS 2.5
Tests not running in same JVM
Done with remote-control plugin
Send a closure for execution in application
compile ":remotecontrol:2.0"
55. GEBCONFIG
Configuration for Geb is placed in GebConfig.groovy
In Grails 3, place it in ´src/integration-test/groovy`
http://www.gebish.org/manual/current/configuration.htm
56. DRIVER
It is possible to configure the browser used.
build.gradle
compile 'org.seleniumhq.selenium:seleniumchromedriver:2.45.0'
compile 'org.seleniumhq.selenium:seleniumfirefoxdriver:2.45.0'
66. SCREENSHOTS
Screenshots and HTML from end of each test:
Extend from GebReportingSpec
class AttendeeFunctionalSpec extends GebReportingSpec
GebConfig.groovy
reportsDir = new File("build/gebreports")
69. EXECUTING JAVASCRIPT
Clicking a button that is hidden will create a
ElementNotVisibleException
<fieldset class="well" style="display: none">
<g:link class="btn" action="index">List</g:link>
</fieldset>