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.
Application under test
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.bat openIdeaif 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)
BasicSpecand implement the first feature. First use the
go()method to navigate http://localhost:3000/. Then use the
$()method the verify there is a
- In the second feature, check if the only
h1on the page has the
- In the third feature the
textshould be "Sign In".
- Since we don't want to re-type that URL all the time, let's define it as
baseUrlfor the suite. Open GebConfig.groovy and set the
baseUrloutside of the
environmentsblock. Implement the three outstanding features by using relative URLs and rely on
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.
LoginPageclasses all extending
geb.Pageand located in
org.gebish.geb.workshop.objective02pagespackage. Set the page
urls and specify an
atchecker on each of them. Implement features in
to(). Use the
at()method in your assertions even though
to()automatically verifies the success of the navigation by calling
at(). Also note that
to()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
objective03waitingpackage. Implement the feature in
WaitingSpec. Go to
RegistrationPagefill 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
objective04contentpackage. Introduce a content definition for the selector used in
atchecker. Introduce content definitions for username, email and password form elements, the submit button and the list of error messages in
RegistrationPageas well as a content definition for the selector used in
atchecker. The implementation of the first feature in
ContentSpecshould 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 to
RegistrationPage, fill out the email and password as well as the username and check that you end up at
HomePageafter 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
objective05modulespackage. Add an
geb.Modulewith a content definition for author username. Add a content definition to
HomePagefor a list of article previews using
moduleList()method. Implement the feature in
ModulesSpecby going to
HomePage, grouping previews by author and then asserting article counts for each of the authors. There is already some code in
ModulesSpecwhich 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.
HomePageas well as
objective06pagemethodspackage. Add a
LoginPagetaking a username and password which fills out the login form and submits it. Use that new method in
PageMethodsSpecto login at
LoginPageand verify that you end up at
HomePageafterwards. 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
objective07advancedwaitingpackage. Add a content definition to
HomePageselecting 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 for
HomePagesucceeds only after the page is fully loaded even though article previews are resolved asynchronously. Add a check at the end of the
LoginPageto ensure that we are at
HomePage- that way calls to
login()will finish only after the
HomePagehas been rendered even though it happens asynchronously. Actions in the first feature of
AdvancedWaitingSpecshould be the same as in
ModulesSpecbut there should not be any explicit waiting in the spec. Implementation of the second feature should be the same as the one in
- setting a
GebConfig.groovyor system property
- reporting, the
- avoid the
- combination with Testcontainers