Spring Boot Geb

BDD (Behavior-driven development) is a technique very similar to implement UAT (User Acceptance Testing) in a software project. Usually is a good idea to use BDD to reprecent how users can define application behaviour, so in that way you can represent user stories in test scenarios aka. feature testing.

This time I am going to show you how integrate Geb to a Spring Boot application, Geb is a very powerful testing framework written in the Groovy programming language, which follows the BDD methodology.

Let’s start creating a new Spring Boot project with Webflux, Lombok and Thymeleaf as dependencies:

spring init --dependencies=webflux,lombok,thymeleaf --build=gradle --language=java spring-boot-geb

Here is the complete build.gradle file generated:

buildscript {
  ext {
    springBootVersion = '2.0.1.RELEASE'
  repositories {
  dependencies {

apply plugin: 'java'
apply plugin: 'groovy'
apply plugin: 'org.springframework.boot'
apply plugin: 'io.spring.dependency-management'

group = 'com.jos.dem.springboot.geb'
version = '0.0.1-SNAPSHOT'
sourceCompatibility = 1.8

repositories {

dependencies {

Now let’s create a simple POJO retrieve information from an end point using Spring WebFlux.

package com.jos.dem.springboot.geb.model;

import lombok.AllArgsConstructor;
import lombok.NoArgsConstructor;
import lombok.ToString;
import lombok.Data;

public class Person {

  private String nickname;
  private String email;


Lombok is a great tool to avoid boilerplate code, for knowing more please go here

Next step is to create methods to list, create and save persons:

package com.jos.dem.springboot.geb.controller;

import static org.springframework.web.bind.annotation.RequestMethod.GET;
import static org.springframework.web.bind.annotation.RequestMethod.POST;

import com.jos.dem.springboot.geb.model.Person;
import com.jos.dem.springboot.geb.service.PersonService;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.RequestMapping;

public class PersonController {

  private PersonService personService;

  private Logger log = LoggerFactory.getLogger(this.getClass());

  public String persons(final Model model){
    log.info("Listing persons");
    model.addAttribute("persons", personService.getAll());
    return "list";

  @RequestMapping(method=GET ,value="create")
  public String create(final Model model){
    log.info("Creating person");
    model.addAttribute("person", new Person());
    return "create";

  public String save(final Person person, final Model model){
    log.info("Saving person");
    model.addAttribute("persons", personService.getAll());
    return "list";


In order to complete our basement let’s create PersonService to bring data:

package com.jos.dem.springboot.geb.service;

import com.jos.dem.springboot.geb.model.Person;

import reactor.core.publisher.Flux;
import reactor.core.publisher.Mono;

public interface PersonService {
  Flux<Person> getAll();
  Mono<Person> getByNickname(String nickname);
  void save(Person person);


package com.jos.dem.springboot.geb.service.impl;

import java.util.HashMap;
import java.util.Map;

import com.jos.dem.springboot.geb.model.Person;
import com.jos.dem.springboot.geb.service.PersonService;

import org.springframework.stereotype.Service;

import reactor.core.publisher.Flux;
import reactor.core.publisher.Mono;

public class PersonServiceImpl implements PersonService {

  private Map<String, Person> persons = new HashMap<String, Person>();

  public Flux<Person> getAll(){
    return Flux.fromIterable(persons.values());

  public Mono<Person> getByNickname(String nickname){
    return Mono.just(persons.get(nickname));

  public void save(Person person){
    persons.put(person.getNickname(), person);


Now all is set in our service, let’s continue adding Groovy, Geb and Spock as dependencies in our build.gradle file:

testCompile("io.github.bonigarcia:webdrivermanager:1.5.0") {
  exclude group: 'org.seleniumhq.selenium'

Geb by convention will look for a src/test/resources/GebConfig.groovy file which contains web driver definition Firefox by default, another important configuration is reports directory where Geb saves the screenshots and HTML dumps at the end of each test:

import io.github.bonigarcia.wdm.FirefoxDriverManager
import org.openqa.selenium.firefox.FirefoxDriver

reportsDir = 'build/test-reports'

atCheckWaiting = true

driver = {
  new FirefoxDriver()

Next step is to define page object, since is a good idea to separate page layout from logic behaviour:

package com.jos.dem.springboot.geb.pages

import geb.Page

class PersonList extends Page {

  static url = "list"
  static at = { title == "Person List" }
  static content = {


Now we can define our test behaviour using Spock Framework

package com.jos.dem.springboot.geb

import geb.spock.GebReportingSpec
import com.jos.dem.springboot.geb.pages.PersonList

class CreatePersonSpec extends GebReportingSpec {

  void 'should create person'() {
      go "http://localhost:8080/create"

      $("input", name: "nickname").value("josdem")
      $("input", name: "email").value("joseluis.delacruz@gmail.com")
      $("button", name: "submit").click()

      at PersonList


This test represent happy path:

  • given: A url and go instruction that directs the web driver to the create person page.
  • when: Fills out name and email text fields in the form and clicks the submit button.
  • then: Is just making sure we going to the right place. We could add any other assertions.

Geb is using a JQuery like style to access to our html elements, Geb call it Navigator API.

To browse the project go here, to download the project:

git clone https://github.com/josdem/spring_boot_geb.git

To run the project:

gradle bootRun

To test the project:

gradle test

Return to the main article

comments powered by Disqus