Geb provides first class support for functional web testing via integration with popular testing frameworks such as Spock, JUnit, TestNG, EasyB and Cuke4Duke.
The Spock, JUnit and TestNG integrations work fundamentally the same way. They provide subclasses that setup a browser instance that all method calls and property accesses/references resolve against via Groovy’s methodMissing and propertyMissing mechanism.
Recall that the browser instance also forwards any method calls or property accesses/references that it can’t handle to it’s current page object, which helps to remove a lot of noise from the test.
Consider the following Spock spec…
import geb.spock.GebSpec
class FunctionalSpec extends GebSpec {
def "go to login"() {
when:
go "/login"
then:
title == "Login Screen"
}
}
Which is equivalent to…
import geb.spock.GebSpec
class FunctionalSpec extends GebSpec {
def "go to login"() {
when:
browser.go "/login"
then:
browser.page.title == "Login Screen"
}
}
The browser instance is created by the testing integration. The configuration mechanism allows you to control aspects such as the driver implementation and base url.
The Spock, JUnit and TestNG integrations also ship a superclass (the name of the class for each integration module is provided below) that automatically takes reports at the end of test methods with the label “end”. They also set the report group to the name of the test class (substituting “.” for “/”).
The report(String label) browser method is replaced with a specialised version. This method works the same as the browser method, but adds counters and the current test method name as prefixes to the given label.
package my.tests
import geb.spock.GebReportingSpec
class FunctionalSpec extends GebReporting {
def "login"() {
when:
go "login"
username = "me"
report "login screen" // take a report of the login screen
login().click()
then:
title == "Logged in!"
}
}
Assuming a configured reportsDir of reports/geb and the default reporter (i.e. ScreenshotAndPageSourceReporter), we would find the following files:
reports/geb/my/tests/FunctionalSpec/1-1-login-login screen.htmlreports/geb/my/tests/FunctionalSpec/1-1-login-login screen.pngreports/geb/my/tests/FunctionalSpec/1-2-login-end.htmlreports/geb/my/tests/FunctionalSpec/1-2-login-end.pngThe report file name format is:
«test method number»-«report number in test method»-«test method name»-«label».«extension»
Reporting is an extremely useful feature and can help you diagnose test failures much easier. Wherever possible, favour the use of the auto reporting base classes.
The Spock, JUnit and TestNG integrations will automatically clear the browser’s cookies at the end of each test method. For JUnit 3 this happens in the tearDown() method in geb.junit3.GebTest, for JUnit 4 it happens in an @After method in geb.junit4.GebTest and for TestNG it happens in an @AfterMethod method in geb.testng.GebTest.
The geb.spock.GebSpec class will clear the cookies in the cleanup() method unless the spec is @Stepwise, in which case they are cleared in cleanupSpec() (meaning that all feature methods in a stepwise spec share the same browser state).
This auto clearing of cookies can be disabled via configuration.
The following table illustrates the specific jars and class names for Spock and JUnit.
| Framework | JAR | Base Class | Reporting Base Class |
|---|---|---|---|
| Spock | geb-spock | geb.spock.GebSpec | geb.spock.GebReportingSpec |
| JUnit 4 | geb-junit4 | geb.junit4.GebTest | geb.junit4.GebReportingTest |
| JUnit 3 | geb-junit3 | geb.junit3.GebTest | geb.junit3.GebReportingTest |
| TestNG | geb-testng | geb.testng.GebTest | geb.testng.GebReportingTest |
The following projects can be used as starting references:
Geb’s EasyB support is based around the binding management features discussed earlier.
EasyB stories are implemented as Groovy scripts with a binding,
and the Geb plugin simply integrates Geb’s BindingUpdater into the EasyB lifecycle.
The Geb EasyB plugin is currently under-developed and is in need of some attention. If you’d like to see better EasyB integration consider getting involved in its development.
Here’s a quick example…
using "geb" // EasyB syntax for using plugins
scenario "using geb", {
given "our base url", {
baseUrl = "http://my.app"
}
when "we go to the page", {
to SomePage
}
then "we arrive at the page", {
at SomePage
}
and "can use the javascript interface", {
js.someJsVariable.shouldBe 1
}
and "can do some waiting", {
waitFor { $("p").text() == "done" }
}
and "can work with the page", {
page.div.text().shouldBe "d1"
}
}
class SomePage extends geb.Page {
static content = {
div { $("#d1") }
}
}
Configuration is done in the given block of a scenario or story. Here you can optionally set 3 properties; driver, baseUrl and browser.
You can set the driver property to the driver instance that you want to implicitly created browser instance to use. However, using the configuration mechanism for driver implementation is preferred.
You can set the baseUrl property to the base url that you want to implicitly created browser instance to use. However, using the configuration mechanism for base url is preferred.
For fine grained control, you can create your own browser instance and assign it to the browser property. Otherwise, an implicit browser object is created using driver and/or baseUrl if they were explicitly set (otherwise the configuration mechanism is used.)
Geb doesn’t offer any explicit integration with Cuke4Duke but due to Cuke4Duke’s use of Groovy scripts, Geb’s binding management features can be used to great effect.
The following is an example of what is possible…
import static org.junit.Assert.*
import static org.junit.matchers.JUnitMatchers.*
import pages.*
this.metaClass.mixin(cuke4duke.GroovyDsl)
Given(~"I am on the Google search page") { ->
to GoogleHomePage
waitFor { at GoogleHomePage }
}
When(~"I search for \"(.*)\"") { String query ->
page.searchField.value(query)
page.searchButton.click()
}
Then(~"I am at the results page") {
assert at(GoogleResultsPage)
}
Then(~"The first link should be \"(.*)\"") { String text ->
waitFor { page.results }
assertThat page.resultLink(0).text(), containsString(text)
}
The following projects can be used as starting references:
The Book of Geb - 0.9.0-RC-3 - April, 2013
Licensed under the Apache License, Version 2.0