A library to support using Testcontainers in Apache Geb integration testing.
This library integrates Geb with Testcontainers to make it easy to write functional tests for your applications and utilize browsers running in testcontainers and optionally record the browser using a VNC testcontainer and/or capture reporting screenshots and HTML.
Much of this library was derived from the Apache Grails - Grails-Geb module which is used to test Grails applications with Geb using Testcontainers.
We have modified the library to be able to test any web application running on localhost, not just a Grails application.
The library contains class names and configuration settings that contain Grails. We are leaving those as-is for now. That may change in major version updates.
We have a sample project using this library and this project has integration tests as well.
For further reference please see the Geb documentation.
To use the library, add the following dependencies to your build.gradle file (adjust for the source set you're using it in, e.g., testImplementation or integrationTestImplementation.):
dependencies {
implementation "net.codebuilders:geb-container:<latest release>"
}There are two ways to use this library. Either extend your test classes with the ContainerGebSpec class or with the GebSpec class.
By extending your test classes with ContainerGebSpec, your tests will automatically use a containerized browser using Testcontainers.
This requires a compatible container runtime to be installed, such as:
- Docker Desktop
- OrbStack - macOS only
- Rancher Desktop
- podman desktop
- Colima - macOS and Linux
If you choose to use the ContainerGebSpec class, as long as you have a compatible container runtime installed, you don't need to do anything else.
Just run ./gradlew <your test task with ContainerGebSpec tests> and a container will be started and configured to start a browser that can access your application under test.
Without any additional configuration you will get a Firefox browser container testing a base URL of http://localhost on port 8080. Firefox was chosen for docker-selenium compatibility with x86_64 and aarch64 architectures.
The default container browser is Firefox. To configure a different browser you can specify a geb.env variable to the integrationTest task in Gradle:
./gradlew integrationTest -Dgeb.env=chromeReference the geb-container-sample GebConfig.groovy on how to setup the environment configuration.
Parallel execution of ContainerGebSpec specifications is not currently supported.
The annotation ContainerGebConfiguration exists to customize the connection the container will use to access the application under test.
The annotation is not required and ContainerGebSpec will use the default values in this annotation if it's not present.
The interface IContainerGebConfiguration exists as an inheritable version of the annotation.
To configure reporting, enable it using the recording property on the annotation ContainerGebConfiguration. The following system properties exist for reporting configuration:
grails.geb.reporting.directory- purpose: if the test enables reporting, the directory to save the reports relative to the project directory
- defaults to
build/gebContainer/reports
By default, no test recording will be performed. Various system properties exist to change the recording behavior. To set them, you can set them in your build.gradle file like so:
tasks.withType(Test).configureEach {
useJUnitPlatform()
systemProperty('grails.geb.recording.mode', 'RECORD_ALL')
}-
grails.geb.recording.mode- purpose: which tests to record
- possible values:
SKIP,RECORD_ALL, orRECORD_FAILING - defaults to
SKIP
-
grails.geb.recording.directory- purpose: the directory to save the recordings relative to the project directory
- defaults to
build/gebContainer/recordings
-
grails.geb.recording.format- purpose: sets the format of the recording
- possible values are
FLVorMP4 - defaults to
MP4
Uploading a file is more complicated for Remote WebDriver sessions because the file you want to upload is likely on the host executing the tests and not in the container running the browser. For this reason, this plugin will setup a Local File Detector by default.
To customize the default, either:
- Create a class that implements
ContainerFileDetectorand specify its fully qualified class name in aMETA-INF/services/grails.plugin.geb.ContainerFileDetectorfile on the classpath (e.g.,src/integration-test/resources). - Use the
ContainerGebConfigurationannotation and set itsfileDetectorproperty to yourContainerFileDetectorimplementation class.
Alternatively, you can access the BrowserWebDriverContainer instance via
the container from within your ContainerGebSpec to, for example, call .copyFileToContainer().
An Example of this can be seen in ContainerSupport#createFileInputSource utility method.
The following system properties exist to configure timeouts:
grails.geb.atCheckWaiting.enabled- purpose: if
atchecks should wait for the page to be in the expected state (uses configured waiting timeout values) - type: boolean
- defaults to
false
- purpose: if
grails.geb.timeouts.retryInterval- purpose: how often to retry waiting operations
- type: Number
- defaults to
0.1seconds
grails.geb.timeouts.waiting- purpose: amount of time to wait for waiting operations
- type: Number
- defaults to
5.0seconds
grails.geb.timeouts.implicitlyWait- purpose: amount of time the driver should wait when searching for an element if it is not immediately present.
- type: int
- defaults to
0seconds, which means that if an element is not found, it will immediately return an error. - Warning: Do not mix implicit and explicit waits. Doing so can cause unpredictable wait times. Consult the Geb and/or Selenium documentation for details.
grails.geb.timeouts.pageLoad- purpose: amount of time to wait for a page load to complete before throwing an error.
- type: int
- defaults to
300seconds
grails.geb.timeouts.script- purpose: amount of time to wait for an asynchronous script to finish execution before throwing an error.
- type: int
- defaults to
30seconds
Selenium integrates with OpenTelemetry to support observability and tracing out of the box. By default, Selenium enables tracing.
This plugin, however, disables tracing by default since most setups lack an OpenTelemetry collector to process the traces.
To enable tracing, set the following system property:
grails.geb.tracing.enabled- possible values are
trueorfalse - defaults to
false
- possible values are
This allows you to opt in to tracing when an OpenTelemetry collector is available.
Provide a GebConfig.groovy on the test runtime classpath (commonly src/integration-test/resources, but any location on the test classpath works) to customize the browser.
To make this work, ensure:
- The
driverproperty in yourGebConfigis aClosurethat returns aRemoteWebDriverinstance. - You set a custom
containerBrowserproperty so thatContainerGebSpeccan start a matching container (e.g. "chrome", "edge", "firefox"). For a list of supported browsers, see the Testcontainers documentation. - Your
build.gradleincludes the driver dependency for the chosen browser.
Example GebConfig.groovy:
driver = {
new RemoteWebDriver(new FireFoxOptions())
}
containerBrowser = 'firefox'Example build.gradle:
dependencies {
integrationTestImplementation 'org.seleniumhq.selenium:selenium-firefox-driver'
}The Grails project uses an @Integration annotation on container tests that injects the port into the test class, but we had to create a different approach.
There are three ways to set the localhost port to test.
- Do nothing and the default is
8080. - Add a
hostPort = 8090setting to GebConfig.groovy. This setting will override the default whenever it is picked up on the test classpath. - Add a class level field to your container test class e.g.
int hostPort = 8000. This will override both the default and the GebConfig.groovy for this test class. Note: this only works at the class level and not inside of a test method because it is only checked when the test class is invoked before any test setup or test is ran.
The Grails project always used the localhost as the baseUrl setting for the container to use and any setting from the GebConfig was ignored.
There are three ways to set a baseUrl to test.
- Do nothing and the default is
http://localhost:$hostPort. - Add a
baseUrl = "http://groovy.apache.org"setting to GebConfig.groovy. This setting will override the default whenever it is picked up on the test classpath. - Add a class level field to your container test class e.g.
String baseUrl = "http://groovy.apache.org". This will override both the default and the GebConfig.groovy for this test class. Note: this only works at the class level and not inside of a test method because it is only checked when the test class is invoked before any test setup or test is ran.
hostPort setting is ignored anytime a baseUrl is configured.