In this workshop we'll introduce the participants to the Geb framework and how to use it for real groovy browser automation.
We will make use of the Spock framework for the tests but won't go into much detail on that.
In this workshop we will be using an instance of RealWorld example app which is dockerised based on a docker-compose.yaml file. We will be using the React + MobX version of the frontend which is available on docker hub based on this clone repository. For the backend, we will be using the SpringBoot version which is also available on docker hub and it's based on this clone repository.
- To import the project into IntelliJ simply run
./gradlew openIdea
(orgradlew.bat openIdea
if you're using Windows) in the root of the repository - To start up the application run
docker-compose up
- the application will be available on localhost at port 3000. - How does this project work (see GebConfig.groovy, build.gradle)
- Open
BasicSpec
and implement the first feature. First use thego()
method to navigate http://localhost:3000/. Then use the$()
method the verify there is adiv
with classhome-page
. - In the second feature, check if the only
h1
on the page has thetext
"Sign Up". - In the third feature the
h1
'stext
should be "Sign In". - Since we don't want to re-type that URL all the time, let's define it as
baseUrl
for the suite. Open GebConfig.groovy and set thebaseUrl
outside of theenvironments
block. Implement the three outstanding features by using relative URLs and rely onbaseUrl
.
So now we already visited three different pages and implemented checks to verify we are at these pages. Next we would want to interact with these pages, which would lead to a lot of duplicate code.
- Create
HomePage
,RegistrationPage
andLoginPage
classes all extendinggeb.Page
and located inorg.gebish.geb.workshop.objective02pages
package. Set the pageurl
s and specify anat
checker on each of them. Implement features inAtCheckerSpec
by usingto()
. Use theat()
method in your assertions even thoughto()
automatically verifies the success of the navigation by callingat()
. Also note thatto()
returns an instance of the page object class passed to it.
Especially on single page applications, some content is loaded dynamically. This means that they will show up eventually, but we cannot tell when. When using such a page as a human, you might not even notice, but Geb will try to find the element immediately and will fail if it is not there, yet.
- Copy the
RegistrationPage
fromobjective02pages
package toobjective03waiting
package. Implement the feature inWaitingSpec
. Go toRegistrationPage
fill out the email and password fields and submit the form. Check for an error message and verify it starts with "username can't be blank". Since the error message is loaded dynamically, you will need to wait for it to appear.
So far we've been using inline css selectors in specs. This can be very repetitive the more you interact with content in tests and that's why Geb has a concept of content definitions to remove the need for such duplication. It also means that if the selectors change for any reason then they have to be changed only in one place and not for each tests in which they are used.
- Copy the
HomePage
andRegistrationPage
fromobjective02pages
package toobjective04content
package. Introduce a content definition for the selector used inHomePage
'sat
checker. Introduce content definitions for username, email and password form elements, the submit button and the list of error messages inRegistrationPage
as well as a content definition for the selector used inat
checker. The implementation of the first feature inContentSpec
should be very similar to the implementation of the feature from the previous objective but it should use the newly introduced content definitions. Use the password, username and email values already provided in the spec. For the second feature, go toRegistrationPage
, fill out the email and password as well as the username and check that you end up atHomePage
after submitting the form.
Sometimes web applications' pages consist of several interesting components. E.g. the page's header or navigation or a form with multiple inputs. We refer to these components in natural language. E.g. "Fill the registration form." Geb's modules are a way to achieve that. Modules are not only great for complex content, but also for repeating content.
- Copy the
HomePage
fromobjective04content
package toobjective05modules
package. Add anArticlePreview
class extendinggeb.Module
with a content definition for author username. Add a content definition toHomePage
for a list of article previews usingNavigator
'smoduleList()
method. Implement the feature inModulesSpec
by going toHomePage
, grouping previews by author and then asserting article counts for each of the authors. There is already some code inModulesSpec
which deals with bootstrapping article data for you.
Pages and modules are not only useful for abstracting selectors out into content definitions. It's also useful to declare methods on pages and modules which contain common actions. Such methods can help to avoid duplication and make tests more readable.
- Copy
LoginPage
fromobjective02pages
package andHomePage
as well asArticlePreview
module fromobjective05modules
package toobjective06pagemethods
package. Add alogin()
method toLoginPage
taking a username and password which fills out the login form and submits it. Use that new method inPageMethodsSpec
to login atLoginPage
and verify that you end up atHomePage
afterwards. Code creating a user is already provided in the spec.
When dealing with asynchronous applications waiting for asynchronous events to complete is very important otherwise the tests will be flakey. A useful technique is to wrap waiting in methods of pages and modules. Waiting should be added immediately after executing an action triggering an asynchronous event. This makes tests appear as if they were synchronous even when they are not. Another benefit is that all team members do not have to exactly understand which parts are asynchronous as long as they use such methods.
- [] Copy
LoginPage
,HomePage
andArticlePreview
fromobjective06pagemethods
package toobjective07advancedwaiting
package. Add a content definition toHomePage
selecting the div with "No articles are here... yet." text which appears when there are no articles. Change the at checker to check if either some article previews or no article available message has been rendered. This will mean that the at checker forHomePage
succeeds only after the page is fully loaded even though article previews are resolved asynchronously. Add a check at the end of thelogin()
method inLoginPage
to ensure that we are atHomePage
- that way calls tologin()
will finish only after theHomePage
has been rendered even though it happens asynchronously. Actions in the first feature ofAdvancedWaitingSpec
should be the same as inModulesSpec
but there should not be any explicit waiting in the spec. Implementation of the second feature should be the same as the one inPageMethodsSpec
.
- setting a
baseUrl
viaGebConfig.groovy
or system property - reporting, the
GebReportingSpec
- defining
environments
wait
presets
- avoid the
StaleElementException
- combination with Testcontainers