Document Properties
Kbid274D10
Last Modified21-Jul-2020
Added to KB29-Jan-2016
Public AccessEveryone
StatusOnline
Doc TypeGuidelines, Concepts & Cookbooks
Product
  • Gradle Tools
  • ICM 7.6
  • Omni-Channel Services Toolset
  • ICM 7.7
  • ICM 7.8
  • ICM 7.9
  • ICM 7.10

Concept - Geb and Spock Tests

1 Introduction

Sustainable software development requires reliable quality metrics based on automated tests. As you can see in the testing pyramid image this happens on different levels of the software. For automated GUI testing Intershop utilizes the specification framework Spock in combination with the web library Geb.

1.1 Glossary

TermDescription
GebGeb is a browser automation solution.
It can be used for scripting, scraping and general automation — or equally as a functional/web/acceptance testing solution via integration with testing frameworks such as Spock or JUnit.
SpockSpock is a testing and specification framework for Java and Groovy applications.

1.2 Documentation

General public documentation is available under http://www.gebish.org/. Information on Spock you can find underhttp://spockframework.github.io/spock/docs.

You may also have a look at the according Cookbook - Geb and Spock Tests (valid to 7.7).

2 Basics

2.1 Spock

Spock is a test and specification framework. Therefore, you can describe the requirements of the expected behavior with this framework. After implementation you can run the requirements against your code, to check automatically whether your requirements are fulfilled or not.

Spock specifications have the following form:

Spock specification
def "This is an example specification" () {
	given: //here you can define preconditions

	when: "This is the example condition"
	then: "This is the conclusion"

	when: "You can repeat the when then clause"
	then: "as often as you need"

	where: //Here you can give some test values. When you run the specification it is executed with all lines which you give here.
		variableName1	|	variableName2
		"variable1.1"	  |	"variable1.2"
		"variable2.1"	  |	"variable2.2"
}

Further information of the behavior you can describe in groovy after the description of the when and than clauses.

Pure Spock specification can be exxecuted with standard JUnit-Runner.

2.2 Geb

Geb is a jQuery-like browser automation solution. It brings some Web drivers along, so you can run the test automatically against different web browsers as Chrome, Firefox, IE, PhantomJs. Geb can be integrated in different test frameworks like Spock, JUnit or TestNG.

Here is an example for a Geb test:

Example Geb test
import geb.Browser
 
Browser.drive {
    go "http://myapp.com/login"
     
    assert $("h1").text() == "Please Login"
     
    $("form.login").with {
        username = "admin"
        password = "password"
        login().click()
    }
     
    assert $("h1").text() == "Admin Section"
}

You can create page objects to easily check that you are on the right page and reuse functionality of the page. A page object is a class, which extends the class "Page" or another page object.

2.3 Spock + Geb

To use Geb in Spock you need to extend your Spock-specification with GebReportingSpec.

You can use page objects like in Geb. Here is an example:

Extend Page Objects
class MyPage extends Page {
    static at = {
        //Here you can check some content, which has to be available e.g.
		$('div', id: 'myId').size() > 0
    }

    static content = {
		//Here you can save some page elements in variables, which you can use in your specification after the check "at MyPage"
		variableName   { $('div', id:'myId') }
    }

    def methodName(variable1, variable2) {
		//With def you define a method, which you can use on the page.
		//When you use def in the method, you can define a local variable.
		def int myInt = variable1
        variableName.$('input', id: 'thisId').value		variable2+myInt
    }
}

class MyPage2 extends MyPage {
	static at = {
		//here you define additional elements to the elements of the static at of "MyPage"
		$('div', id: 'myId2').size() > 0
	}

	//The rest is same like above.
}

This is an example for a specification, which uses the pages from above:

Spock spec using page objects
import MyPage
import MyPage2

class MySpec extends GebReportingSpec {
	def "This is an example specification" () {
		given: //here you can define preconditions

		when: "This is the example condition"
			at MyPage	//check that you are on the defined page "MyPage"
			methodName 1, 'testVariable'	//use the methode from the page object "MyPage"
		then: "This is the conclusion"
			//define some conclusion
			at MyPage2
			
		when: "do something more"
			methodName	variableName1, variableName2	//Use the method from the parent page "MyPage" on "MyPage2". As varaibles you can use the defined variables from the where clause.
		then: "something happens"
			//check that something happens

		where: //Here you can give some test values. When you run the specification it is executed with all lines which you give here.
			variableName1	|	variableName2
			1	  		  	|	"variable1.2"
			2				|	"variable2.2"
	}
}

Use modules to manage generic content at pages:

Modules
import geb.Module
import geb.Page

class MyModule extends Module{
	//Parameter to specify the instance of MyModule
	def param1;

	static content = {
		//structure of all MyModules
		field {$("button",text:param1)}
	}
}

class MyPage extends Page{

	static content = {
		//structure of MyPage based on modules
		modules {term -> module(new MyModule(param1:term)}
	}
}

class MySpec extends GebReportingSpec {
	
	def "This is an example specification" () {
		when: "I click at 'Next' button"
		at MyPage
		modules("Next").field.click()
	}
}

3 Assembly Setup for GUI Tests with Geb and Spock

3.1 Location for Geb Artifacts

Specifications, pages and modules for an assembly are stored within the assembly itself. The ish-assembly plugin provides a SourceSet "remoteTest" for implementing remote tests. Geb Specifications has to be stored within the "geb" package of the RemoteTest SourceSet as shown in the code block below. There are no further restrictions for the package structure. The code block below shows a feasible package structure, which can be used.

The GebConfig.groovy has to be located within the default package of the remoteTest SourceSet. It is recommendet to place it into the resources folder.

remoteTest sourceSet
src
 |-remoteTest
	|-groovy
		|-geb
			|-com.intershop.inspired
				|-b2c
 					|-pages   // place for pages and modules      
					|-specs	  // place for all specs
	|-resources
		|-GebConfig.groovy

3.2 Geb Configuration - GebConfig.groovy

Geb attempts to load a ConfigSlurper script named GebConfig.groovy from the default package. The script allows you to control various aspects of Geb, i.e., timeouts or dealing with error (unexpected) pages. Please read the Configuration section of the Geb Manual in order to learn more about its capabilities. The most important part of the GebConfig Script is the the definition of the WebDriver environments. An example for a GebConfig.groovy script is displayed below.

When loading the GebConfig script, the ish-assembly plugin provides system properties with information about the execution context of the geb Tests-, i.e., the path to the WebDriver executable. The properties listed below are always provided. Additional properties can be configured via the build.gradle of the assembly as described in the following sections.

PropertyDescription
geb.build.reportsDirRoot dir, i.e., for page dumps and othrgeb reports
geb.build.baseUrlURL of the started application server to be tested
geb.envName of the requested webdriver environment
webDriverDirRoot directory of the installed web driver to be used
webDriverExecRelative path to the web driver executable
GebConfig.groovy
import org.openqa.selenium.Dimension
import org.openqa.selenium.chrome.ChromeDriver
import org.openqa.selenium.phantomjs.*
import org.openqa.selenium.remote.*

def gebEnv = System.getProperty("geb.env");
def webDriverDir = System.getProperty("webDriverDir")
def webDriverExec = new File(webDriverDir, System.getProperty("webDriverExec")).absolutePath

waiting {
    timeout = 10
}

environments {
    phantomJsPC {
        driver = {
            def driver = createPhantomJsDriverInstance(webDriverExec)
            driver.manage().window().setSize(new Dimension(1920, 1200))
            page.settings.resourceTimeout = 10000;
            driver
        }
    }
    chromeTablet {
        driver = {
            def driver = createChromeDriverInstance(webDriverExec)
            driver.manage().window().setSize(new Dimension(1024, 768))
            driver
        }
    }
    chromePC {
        driver = {
            def driver = createChromeDriverInstance(webDriverExec)
            driver.manage().window().setSize(new Dimension(1920, 1200))
            driver
        }
    }
}

private def createChromeDriverInstance(String webDriverExec) {
    System.setProperty("webdriver.chrome.driver", webDriverExec)
    driverInstance = new ChromeDriver()
    driverInstance
}

private def createPhantomJsDriverInstance(String webDriverExec) {
    System.setProperty("phantomjs.binary.path", webDriverExec)
    ArrayList cliArgsCap = new ArrayList();
    cliArgsCap.add("--web-security=false");
    cliArgsCap.add("--ssl-protocol=any");
    cliArgsCap.add("--ignore-ssl-errors=true");
    DesiredCapabilities desiredCapabilities = new DesiredCapabilities()
    desiredCapabilities.setCapability(PhantomJSDriverService.PHANTOMJS_CLI_ARGS, cliArgsCap);
    new PhantomJSDriver(desiredCapabilities)
}

3.3 build.gradle Preparation: Dependencies

Depending on the set of Selenium web drivers, you want use for your asembly, you need to add related dependencies to toe remoteTest configuration.

build.gradle - dependencies
dependencies {
    remoteTestCompile "org.seleniumhq.selenium:selenium-support:2.47.+"
    remoteTestCompile "org.seleniumhq.selenium:selenium-chrome-driver:2.47.+"
    remoteTestCompile ("com.codeborne:phantomjsdriver:1.2.+") {
        transitive = false
    }
}

3.4 build.gradle Preparation: gebConfiguration

For executing geb tests from the assembly gradle project, you need to add gebConfiguration section as shown below into your build.gradle. The section is responsible for preparing web drivers and to provide the environment for executing the Geb tests.

There are two sub sections: systemProperties and webDrivers.

You can use the optional systemProperties section for declaring additional systemProperties, which will taken over to your GebConfig.groovy script and the Geb Specifications.

The webDrivers section defines all web drivers, to be supported from your Geb tests:

StructureDescription

<webDriverName>

Identifier for a Selenium web driver to be supported

environments

List of Geb environments, related tot he web driver. Defined environments determines the value of the geb.env parameter, taken over to the GebConfig.groovy script. So the has to match with the environments, defined within the GebConfig.groovy file.

download/<os>

Optional declarations, how the web drivers can be downloaded. If a download section for a web driver and the detected operating system exists, the driver will be downloaded into the gradle cach directory and installed into "project.buildDir/webdriver/<webDriverName>.

If no related downloades section exists, you need to ensure, that the related web driver is installed before the Geb tests are executed.

url

Download URL for the web driver

archiveType

'zip' and 'tar' are supported

webDriverDir

(optional) Path to the extracted web driver. Default is $buildDir/webdriver/<webDriverName>. But depending on the downloades archive structure, it also may be be a subfolder of them.

webDriverExec

Relative path to the driver binary

Depending on your gebConfiguration the following tasks for executing gebTests are are available:

  • For each environment, a task geb<environmentName>EnvironmentTest (type:Test) is available.
  • For each webDriver, a task geb<webDriverName>Test is available, for executing the Geb tests with all related environments.

Example for a gibConfiguration section:

build.gradle - gebConfiguration
gebConfiguration {
	 
	systemProperties {
			locale='en_US'
	}

    webDrivers {
        phantomJsDriver {
            environments {                
                phantomJsPC {check=true}
            }

            download {
                linux {
                    url = 'https://bitbucket.org/ariya/phantomjs/downloads/phantomjs-1.9.8-linux-x86_64.tar.bz2'
                    archiveType = 'tar'
                    webDriverDir = 'phantomjs-1.9.7-linux-x86_64'
                    webDriverExec = 'bin/phantomjs'
                }
                windows {
                    url = 'https://bitbucket.org/ariya/phantomjs/downloads/phantomjs-1.9.7-windows.zip'
                    archiveType = 'zip'
                    webDriverDir = 'phantomjs-1.9.7-windows'
                    webDriverExec = 'phantomjs.exe'
                }
            }
        }

        chromeDriver {
            environments {
                chromePC
                chromeTablet
            }

            download {
                linux {
                    url = 'http://chromedriver.storage.googleapis.com/2.20/chromedriver_linux64.zip'
                    archiveType = 'zip'
                    webDriverExec = 'chromedriver'
                }
                windows {
                    url = 'http://chromedriver.storage.googleapis.com/2.20/chromedriver_win32.zip'
                    archiveType = 'zip'
                    webDriverExec = 'chromedriver.exe'
                }
            }
        }
    }
}

3.5 remoteTest Configuration

The ish-assembly plugin provides a Task remoteTest" for executing tests against a started application environment. A map "remoteTest.env" is defined, which is responsible for storing information, which are required for accessing the environment to be tested. By default, remoteTest.env is preloaded with all properties of your environment.properties. For accessing another environment, i.e., a demoSever enviornment. You can create another environment properties file (i.e., demoServer.properties) and load it with using the buld property "remoteTest.env":

gradlew -PremoteTest.env=demoServer.properties

There are default values for the following environment properties. Hence an value for at lease these properties are always ensured:

PropertyDefault value
hostNamelocalhost
webserverPort80
webserverHttpsPort443

By default, geb Tests are not automatically triggered, when calling the remoteTest tasks. The assignment can be done within the assembly configuration with using remoteTest.dependsOn().

4 Executing Geb Tests

Depending on your gebConfiguration the following tasks for executing gebTests are are available:

  • For each environment, a task geb<environmentName> EnvironmentTest (type:Test) is available.
  • For each webDriver, a task geb<webDriverName> Test is available, for executing the Geb tests with all related environments.

4.1 Available Tasks

  1. Use "gradle tasks --all" for displaying all available tasks.

4.2 Examples for Gradle Commands

Some examples for executing tests from the command line:

Execute all gebtests for a given selenium driver environment (i.e. chromePC)
gradlew gebChromePCEnvironment
A subset of tests can be selected with the --tests option
gradlew gebChromePCEnvironment --tests *Order*
Executing the tests agains a remote server.
gradlew gebChromePCEnvironment -PremoteTest.env=demoServer.properties

4.3 Test Results

After the execution of the tests, the following reports/results are available:

  • xunit test results: target/test-results/<environment>
  • xunit html report: target/reports/remote/geb/<environment>
  • pagedumps (png and html of the last visit page): target/reports/remote/geb/<environment>/pageDumps

5 Useful Commands

See also: http://www.gebish.org/manual/current/

5.1 Find an Element with Identifier

E.g., to find a div with class "test" you can use:

$('div', class: 'test')

5.2 Find Elements with an Attribute Starting with Specific String

E.g., Find all link, which href starts with "test"

$('a[href^="test"]')

5.3 Find Elements with an Attribute Ending with Specific String

E.g., Find all link, which href ends with "test"

$('a[href$="test"]')

5.4 Combine Selectors

You can find specific elements inside a given element by add the next find function with "."

E.g. find all elements "td" in the element "table" with id "tableId" :

$('table', id: "tableId").$('td')

5.5 Execute a Function for an Element

You can execute a function for each found element, when you mark the web object with "*"

E.g., check that table cells from above contains a specific string "Test String".

$('table', id: "tableId").$('td')*.text().contains("Test String")

5.6 Execute a Real jQuery Expression

E.g., make all input element with class "hidden" visible.

js.exec 'jQuery("input[class=hide]").attr("style", "display: block !important")'

5.7 Wait for

E.g., wait for element "div" with class "test" is available:

waitFor { $('div', class: "test").size() > 0	}


5.8 Select Dropdown

E.g., select a dropdown field by value or name.

<form>
    <select name="artist">
        <option value="1">Ima Robot           </option>
        <option value="2">Edward Sharpe and the Magnetic Zeros</option>
        <option value="3">Alexander</option>
    </select>
</form>
 
$("form").artist = "1"   // selects the first option
$("form").artist = 2     // selects the second option
$("form").artist = "Alexander"  // selects the third option
$("[name='artist']").find("option").find{
  it.text().trim() == "Ima Robot"
}       // selects the first option even if there are multiple spaces attached

5.9 Maximize Browser

Some Tests need a fully shown browser. Define your Browser, (e.g., Chrome) like this in GebConfig.groovy:

 chrome {
        driver = {
			...
            def d = new ChromeDriver()
    		...
	        d.manage().window().maximize()
            d
        }
    }

5.10 Specifically Sized Browser

The phantomJS-Browser cannot use the maximize() function. To avoid conflicts, use code like this for specific size:

phantomJs {
        driver = {
            ...
            def d= new PhantomJSDriver()
			...
            d.manage().window().setSize(new Dimension(1028, 768))
            d
        }
  }

5.11 Wait a Defined Time

Although the specific waiting is not in the sense of Spock tests, there may be moments that requires a certain time to wait.

def sleepForNSeconds(int n){
	def originalMilliseconds = System.currentTimeMillis()
    waitFor(n + 1){
    	(System.currentTimeMillis() - originalMilliseconds) > (n * 1000)
    }
}

5.12 Hover Function Test

To simulate a hover effect and click at objects, which spawn after hovering use something like this.

def hover(hoverObject){
	interact{
		moveToElement( hoverObject)
        hoverObject.jquery.show()
    }
}

Disclaimer

The information provided in the Knowledge Base may not be applicable to all systems and situations. Intershop Communications will not be liable to any party for any direct or indirect damages resulting from the use of the Customer Support section of the Intershop Corporate Web site, including, without limitation, any lost profits, business interruption, loss of programs or other data on your information handling system.

Customer Support
Knowledge Base
Product Resources
Support Tickets