Geb

Automatische UI testen: elke webapplicatie zou ze moeten hebben, maar vaak is dit het ondergeschoven kindje van elk project. Oorzaken van het ontbreken van UI testen zijn dat het meestal veel tijd kost om goede UI testen op te zetten en dat het lastig is om ze up-to-date te houden. Ook wordt de taak uitbesteed aan niet-technische testers. Met behulp van de, soms ingewikkeld gevonden, Selenium IDE worden testen geïntroduceerd die eveneens niet efficiënt zijn te onderhouden.

Om automatische UI testen niet langer weg te schuiven, kunnen we Geb gebruiken. In dit artikel maken we kennis met een Geb (uitspraak: ‘jeb’). We kijken naar wat Geb is, hoe het opgebouwd is en hoe je het gemakkelijk kan toepassen binnen jouw project.

 

Geb: De juiste mix

Geb is een combinatie van verschillende tools en technieken, namelijk WebDriver, jQuery selectors, Spock en het zogenaamde Page Object Pattern. Geb is geschreven in Groovy en dat zorgt ervoor dat de onderdelen uitstekend samenwerken. Deze combinatie maakt Geb erg krachtig. Groovy is een taal die gecompileerd wordt naar JVM code. Het is snel te leren en vormt daarmee een krachtige taal om snel en effectief nieuwe programmatuur te bouwen, die voor iemand zonder ontwikkelachtergrond ook redelijk vlot te begrijpen is. Het maakt het daardoor eenvoudig om effectieve tests te schrijven en te onderhouden. Al met al kunnen we met Geb meer en betere UI tests schrijven met minder code.

 

Spock

Naast dat Geb goed te combineren is met TestNG, JUnit of Cucumber, komt het toch het meest tot zijn recht in combinatie met Spock. Spock is een test framework (ook geschreven in Groovy) met een hele heldere syntax en goede ondersteuning voor stubs en mocks. Andere krachtige elementen van Spock zijn:

  • Duidelijke structuur in je test: Elke test bevat een given, when en then block die duidelijk aangeeft wat je test en wat je verwacht.
  • Where data block: Hiermee kun je dezelfde test uitvoeren met daarin verschillende testdata en uitkomsten. Op deze manier is het nodig om voor elk testscenario een aparte test te schrijven.
  • Duidelijke asserts en foutmeldingen: Spock geeft op detail weer waar één van je asserts misgaat en is daarmee duidelijker ten opzichte van bijvoorbeeld JUnit.

In dit artikel zullen we Spock gebruiken als basis voor onze tests.

 

jQuery selectors

Een ander krachtig onderdeel van Geb is het jQuery selector principe. Hiermee kun je met korte krachtige statements elementen op je pagina selecteren door middel van een syntax die erg lijkt op de jQuery selectors. Zo kun je elementen selecteren aan de hand van het id: $("#logo") of bijvoorbeeld aan de hand van het class attribuut: $(".row")

Ook is het mogelijk om meerdere elementen te selecteren op basis van type. Bijvoorbeeld “$("p", 2)” selecteert de derde p tag die we tegenkomen. Veel developers zijn bekend met jQuery, wat het gebruik van Geb makkelijker maakt.

 

Configuratie

Geb heeft een configuratie file nodig waarin je alle configuratie voor onze tests kan definiëren. Hiervoor is de GebConfig.groovy file in het leven geroepen. Dit bestand kun je in je src/test/resources directory opslaan. Je kunt bijvoorbeeld aangeven welke browsers je wilt testen, wat de basis URL is voor je webapplicatie of instellingen configureren van een eventuele proxy. In listing 1 zie je een voorbeeld van een eenvoudige GebConfig.groovy file.

In de GebConfig.groovy hebben we aangegeven waar onze reports van Geb komen te staan en dat we onze applicatie met Firefoxtesten.


import org.openqa.selenium.firefox.FirefoxDriver
driver = { new FirefoxDriver() }
reportsDir = new File("target/geb-reports")

Listing 1

 

Hello world!

Voor Geb heb je uiteraard enkele dependencies nodig. In dit artikel gebruiken we Gradle als build tool, maar Geb is ook gemakkelijk te integreren in je Maven build. In listing 2 zie je het minimale wat je nodig hebt om met Gradle je Geb testen te kunnen uitvoeren.


apply plugin: 'groovy'
 
sourceCompatibility = 1.5
 
repositories {
   jcenter()
}
 
dependencies {
   testCompile 'org.codehaus.groovy:groovy-all:2.4.1'
   testCompile "org.gebish:geb-core:0.12.2"
   testCompile "org.gebish:geb-spock:0.12.2"
   testCompile("org.spockframework:spock-core:1.0-groovy-2.4") {
       exclude group: "org.codehaus.groovy"
   }
   testCompile "org.seleniumhq.selenium:selenium-firefox-driver:2.45.0"
   testCompile "org.seleniumhq.selenium:selenium-support:2.45.0"
}

Listing 2

We hebben nu genoeg theorie gelezen; het wordt tijd voor wat code! Laten we eens kijken naar een simpele Geb test die is gebaseerd op Spock. In listing 3 zie je een eenvoudige Geb test (of in Spock termen te blijven: GebSpec). De specificatie extenden we met de class GebReportingSpec. Er bestaat ook de class GebSpec, maar GebReportingspec heeft als voordeel dat het ook de reporting verzorgt. Zo kun je bijvoorbeeld – door middel van configuratie in je GebConfig.groovy file – aangeven dat Geb screenshots maakt wanneer één van je tests faalt.


class HelloWorldSpec extends GebReportingSpec {

def "Hello world!"() {
when:
go "http://www.google.com/webhp?complete=0"
$("[name=q]").value("Hello world program!")
$("[name=btnI]").click()

then:
driver.currentUrl.startsWith( "https://en.wikipedia.org/wiki/%22Hello,_World")
}
}

Listing 3

Het scenario spreekt eigenlijk voor zich. Met het go statement besturen we de Browser en geven we aan naar welke URL we willen navigeren. We selecteren het inputveld op de Google pagina en voeren onze tekst in. Vervolgens klikken we op de ‘Feeling lucky’ knop. In het then block doen we onze assert en controleren of de URL begint met Wikipedia.

We kunnen nu onze test uitvoeren met gradle test. Let wel op dat je de Firefox browser hebt geïnstalleerd, zoals geconfigureerd in de GebConfig.groovy file.

 

Pages en Modules

Wat Geb daarnaast krachtig maakt, is het gebruik van het Page Object Model. Dit is een onderdeel van Selenium, maar dankzij Geb is het nu ook toe te passen vanuit Groovy code. Pages zijn eigenlijk definities van je pagina’s. Het grote voordeel van het gebruik van Pages en Modules is dat we ze kunnen gebruiken in elke test. Dit scheelt veel dubbele code. Mocht de pagina wijzigen, dan hoef je dit slechts op één plek aan te passen. In listing 4 zie je een voorbeeld van een Page die de Google zoekpagina beschrijft.

Om een Page te maken extend je de class geb.Page. Een Page is opgebouwd uit 3 onderdelen:

  • Een static attribuut dat een expressie bevat. Deze expressie bepaalt wanneer de pagina geladen is.
  • Een static attribuut dat verwijst naar de suffix van de pagina en wordt gecombineerd met je die geconfigureerd is in je file. Hier kun je ook een complete URL in zetten.
  • In het static attribuut definieer je content die op de pagina staat. De content kun je lokaliseren door middel van de jQuery selectors.

package page
 
import geb.Page
 
class GoogleSearchPage extends Page {
 
   static url = "http://www.google.com/webhp?complete=0"
 
   static at = { searchBox.displayed }
 
   static content = {
       searchBox { $("[name=q]") }
       feelingLuckyButton { $("[name=btnI]")}
   }
}

Listing 4

Modules zijn onderdelen binnen een pagina. Dit kun je bijvoorbeeld gebruiken als een stuk functionaliteit terugkomt op meerdere pagina’s. Om een module te maken, extend je de class geb.Module. Een module heeft ook een static content attribuut waar je de content op je module kan definiëren.

Pages en modules kun je uitbreiden met methoden die bepaalde acties voor je invullen die je vervolgens ook weer kan gebruiken in je verschillende tests. We kunnen een Page object ook uitbreiden met methoden, zoals bijvoorbeeld die in listing 5, zodat de code duidelijk en te hergebruiken is.


  void feelingLuckyFor(String query) {
       searchBox.value(query)
       feelingLuckyButton.click()
 }

Listing 5

We kunnende Google Page class gebruiken om te valideren op welke pagina je belandt wanneer je bij een bepaalde zoekterm de “Feeling lucky” knop indrukt. Wanneer je dit combineert met de data tables van Spock kunnen we een hele krachtige combinatie maken die binnen één test allerlei verschillende zoektermen kan testen. De test die dit uitvoert, zie je terug in listing 6.


import geb.spock.GebReportingSpec
import page.GoogleSearchPage
import spock.lang.Unroll
 
class GoogleLuckySearchSpec extends GebReportingSpec {
 
   @Unroll
   def "When I search for #searchTerm, feeling lucky results in #firstHit"() {
 
       given:
       to GoogleSearchPage
 
       when:
       page.feelingLuckyFor(searchTerm)
 
       then:
       driver.currentUrl.startsWith(firstHit)
 
       where:
       searchTerm       | firstHit
       "NLJUG"          | "http://www.nljug.org"
       "JDriven"        | "http://www.jdriven.nl"
       "Geb UI testing" | "http://www.gebish.org"
   }
}

Listing 6

 

Asserts

Het principe achter een krachtige test is natuurlijk dat je op de juiste manier je functionaliteit kan verifiëren. Geb ondersteunt een breed scala aan manieren om functionaliteit te verifiëren. Zo kun je bijvoorbeeld testen of iets getoond wordt, of iets de juiste afmetingen of positie heeft of zelfs dat aan een element de juiste CSS class hangt. In listing 7 zie je een Groovy script dat door middel van Geb enkele asserts maakt op de JDriven website.


import geb.Browser
 
Browser.drive {
   go "http://www.jdriven.nl"
 
   assert $("h1").text() == "JDriven"
   assert $("h1").height == 92
   assert $("h1").y == 15
   assert $("h1").x == 220
   assert $("h1").displayed
   assert $("h1").classes() == ["logo"]
   assert $("h1").css("margin-top") == "15px"
}

Listing 7

 

AJAX calls

Ook wanneer je pagina dynamisch is opgebouwd, kun je Geb gebruiken voor je UI tests. We kunnen bijvoorbeeld eigenschappen geven aan elementen in het content block. In listing 8 zie je een Page class waar twee elementen zijn gedefinieerd. Één element wordt bij het laden van de pagina asynchroon geladen; de andere is initieel niet zichtbaar.


class DynamicPageWithWaiting extends Page {
static content = {
dynamicallyAdded(wait: true) { $("p.dynamic") } initiallyNotVisible(required: false) { $("p.notVisible") }
}
}

Listing 8

We kunnen in de test ook aangeven binnen hoeveel tijd het element getoond moet worden. Mocht dit langer duren dan de opgegeven tijd, dan faalt de test. Dit heeft als voordeel dat je test niet héél lang op een response staat te wachten. Ook kun je hiermee tijdens je tests de snelheid van de applicatie gedeeltelijk waarborgen. We kunnen meerdere time-outs gebruiken door deze te specificeren in de GebConfig.groovy file. Standaard zal Geb uitgaan van de default waardes, namelijk een time-out van 10 seconden en een retryInterval van een halve seconde.

We kunnen ook verschillende waiting profielen definiëren in de GebConfig.groovy, zoals in listing 9.


//GebConfig.groovy
waiting {
presets {
slow {
timeout = 20
retryInterval = 1
}
quick {
timeout = 1
}
}
}

Listing 9

De profielen kun je vervolgens weer in je test gebruiken. In listing 10 zie je een voorbeeld van een test waar een AJAX call gedaan wordt.


import geb.spock.GebSpec
 
class UpdateContentSpec extends GebSpec {
 
   def "verify content is updated"() {
       given:
       go "http://www.w3schools.com/ajax/ajax_example.asp"
 
       when:
       $(".w3-example button").click()
       waitFor {
           $("#myDiv").children()[0].text() ==
               "AJAX is not a new programming language."
        }
 
       then:
       $("#myDiv").children()[1].text() ==
           "AJAX is a technique for creating fast and dynamic web pages."
   }
}

Listing 10

 

Build integratie

Het is ook mogelijk om je UI tests op verschillende omgevingen uit te voeren. Dit kan handig zijn als je de tests lokaal wilt uitvoeren, maar ook op je test- of acceptatieomgeving. Hiervoor kun je meerdere omgevingen definiëren in je GebConfig.groovy file. Je kunt hier een andere baseUrl property opgeven, maar het is ook mogelijk om verschillende browsers op te geven per omgeving. Zo kun je bijvoorbeeld ervoor kiezen om lokaal alleen in Chrome te testen en op je testomgeving op meerdere browsers. Een voorbeeld van een GebConfig.groovy file met verschillende omgevingen gedefinieerd zie je in listing 11. Hierin is voor drie omgevingen een verschillende baseUrl gedefinieerd.


import org.openqa.selenium.firefox.FirefoxDriver
 
driver = { new FirefoxDriver() }
 
reportsDir = new File("target/geb-reports")
 
environments {
   local {
       baseUrl = 'http://localhost:8080/my-app'
   }
   test {
       baseUrl = 'http://test-server.nl/my-app'
   }
   acceptance{
       baseUrl = 'http://acceptance-server.nl/my-app'
   }
}

Listing 11

We kunnen door de geb.env Java system property mee te geven in onze build de test uitvoeren voor die specifieke omgeving. Voor Gradle doe je dat als volgt: gradle test -Dgeb.env=test.

 

Conclusie

Het Geb framework wordt steeds populairder. Door de eenvoudige Groovy syntax kun je binnen een korte tijd uitgebreide en goed onderhoudbare UI testen schrijven die ook goed te begrijpen zijn voor niet-programmeurs. Naast de functionaliteit die in dit artikel beschreven is, ondersteunt het ook functionaliteit voor het testen van file downloads, JavaScript, etc.

Het feit dat Geb in Groovy geschreven is en je applicatie in Java of een andere taal, belemmert je niet om Geb te gaan gebruiken. Je kunt Geb gemakkelijk naast je project zetten om je UI te testen. Door de goede support voor AJAX requests en uitgebreide set aan asserts kun je vrijwel elk onderdeel van je UI testen.

Op dit moment is de laatste versie 0.12.2, wat betekent dat er nog geen major release is geweest. De onderdelen van het framework zijn echter al volwassen genoeg om goed en eenvoudig in te kunnen zetten. Daarnaast is er nog de uitgebreide en duidelijke documentatie die je gemakkelijk op weg helpt. Voor iedereen die op zoek is naar een goed framework voor hun automatische UI testen, is Geb zeker de moeite waard om eens goed naar te kijken.