How to configure Geb/Spock with Gradle

Geb/Spock + Gradle

Well, it turns out you have to use the right version of geb-core, geb-spock and┬áspock-core, not to mention the right version of groovy. The problem appears to be that that Geb/Spock integration jar (geb-spock:0.7.2) was built with using a slightly Groovy 1.8, and the 2.x series just hasn’t caught up yet. This means trying to get Geb/Spock on Gradle 2.x working just won’t work – you will get ClassNotFoundExceptions and an urge to pull your hair out. After digging around and trying various combinations I finally settled for Spock 0.6 on Gradle 1.8, Geb/Spock 0.7.2, and Geb 0.7.2. Note that the Geb/Spock integrations should run the same version. My gradle dependencies wound up looking like this:

dependencies {

	def seleniumVersion = "2.42.2"
	def phantomJsVersion = '1.1.0'
	def cargoVersion = '1.4.9'

	// selenium drivers
	compile "org.seleniumhq.selenium:selenium-ie-driver:$seleniumVersion"
	compile "org.seleniumhq.selenium:selenium-chrome-driver:$seleniumVersion"
	compile "org.seleniumhq.selenium:selenium-firefox-driver:$seleniumVersion"
	compile "org.seleniumhq.selenium:selenium-support:$seleniumVersion"
	compile("com.github.detro.ghostdriver:phantomjsdriver:$phantomJsVersion") {
		transitive = false
	}

	// geb
	compile 'org.codehaus.geb:geb-core:0.7.2'
	compile 'org.codehaus.geb:geb-spock:0.7.2'

	// spock
	compile 'org.spockframework:spock-core:0.6-groovy-1.8'

	compile 'junit:junit:4.8.2'
	compile 'org.slf4j:slf4j-log4j12:1.7.6@jar'
	compile 'org.slf4j:slf4j-api:1.7.6@jar'

}

I wanted to create a separate task just to run these Geb/Spock tests so did the following:

task acceptanceTest(type: Test, dependsOn: [compileTestGroovy]) {

	maxParallelForks = 5
	forkEvery = 5

	include 'com/something/acceptance/**'

	doFirst {
		println '\nStarting tomcat via cargo'
		tasks.cargoStartLocal.execute()
	}

	doLast {
		println '\nStopping tomcat via cargo'
		tasks.cargoStopLocal.execute()
	}

	def timestamp

	beforeTest { descriptor ->
		timestamp = new Date()
	}

	afterTest { desc, result ->
		logger.lifecycle("\n\n>>> Running " + "${desc.name} [${desc.className}]")
		println "Executed ${desc.name} [${desc.className}] with result: " +
			"${result.resultType} in ${new Date().getTime() - timestamp.getTime()}ms"
	}

}

Since my Geb tests are written in groovy, I’ve structured my project such that my acceptance tests are in the proper groovy directory, and now I can now run Geb tests just like regular Unit and Integration tests. Heck, I could even bundle cargo with it and have it run my application, fire up the Geb/Spock Tests and then shut down the app in one fell swoop. Final script looks like this:

buildscript {
	repositories {
		jcenter()
	}
	dependencies {
		classpath 'com.bmuschko:gradle-cargo-plugin:2.0.3'
	}
}

apply plugin: 'java'
apply plugin: 'groovy'
apply plugin: 'com.bmuschko.cargo'

repositories {
	jcenter()
	mavenCentral()
}

dependencies {

	def seleniumVersion = "2.42.2"
	def phantomJsVersion = '1.1.0'
	def cargoVersion = '1.4.9'

	// selenium drivers
	compile "org.seleniumhq.selenium:selenium-ie-driver:$seleniumVersion"
	compile "org.seleniumhq.selenium:selenium-chrome-driver:$seleniumVersion"
	compile "org.seleniumhq.selenium:selenium-firefox-driver:$seleniumVersion"
	compile "org.seleniumhq.selenium:selenium-support:$seleniumVersion"
	compile("com.github.detro.ghostdriver:phantomjsdriver:$phantomJsVersion") {
		transitive = false
	}

	// geb
	compile 'org.codehaus.geb:geb-core:0.7.2'
	compile 'org.codehaus.geb:geb-spock:0.7.2'

	// spock
	compile 'org.spockframework:spock-core:0.6-groovy-1.8'

	// cargo support
	cargo "org.codehaus.cargo:cargo-core-uberjar:$cargoVersion",
		"org.codehaus.cargo:cargo-ant:$cargoVersion"

	compile 'junit:junit:4.8.2'
	compile 'org.slf4j:slf4j-log4j12:1.7.6@jar'
	compile 'org.slf4j:slf4j-api:1.7.6@jar'

}

// == test configurations == //

task acceptanceTest(type: Test, dependsOn: [compileTestGroovy]) {

	maxParallelForks = 5
	forkEvery = 5

	include 'com/something/acceptance/**'

	doFirst {
		println '\nStarting tomcat via cargo'
		tasks.cargoStartLocal.execute()
	}

	doLast {
		println '\nStopping tomcat via cargo'
		tasks.cargoStopLocal.execute()
	}

	def timestamp

	beforeTest { descriptor ->
		timestamp = new Date()
	}

	afterTest { desc, result ->
		logger.lifecycle("\n\n>>> Running " + "${desc.name} [${desc.className}]")
		println "Executed ${desc.name} [${desc.className}] with result: " +
			"${result.resultType} in ${new Date().getTime() - timestamp.getTime()}ms"
	}

}

// == cargo configuration == //

cargo {
	containerId = 'tomcat7x'
	port = 8080

	deployable {
		file = file("target/path/to/application.war")
		context = "/"
	}

	local {
		installer {
			installUrl = 'http://archive.apache.org/dist/tomcat/tomcat-7/v7.0.54/bin/apache-tomcat-7.0.54.zip'
			downloadDir = file("tomcat/download")
			extractDir = file("tomcat/extract")
		}
	}
}

As you can see, before the Geb tests run, I invoke the cargoStartLocal task to fire up tomcat7, and I’ve configured cargo such that it will download tomcat7 from apache, extract the archive, and then deploy my war file on port 8080. Once the Geb tests complete, cargo will shut down the app, and my automated acceptance tests will be complete.

Happy testing!