Grails Tests with Cucumber Cont'd
My previous blog post has been about introducing Cucumber in a Grails application. This post is a follow up on a particular requirement that came up in the course of writing the first Cucumber specifications.
One point of criticism was the lack of auto-complete support for Geb methods and variables which are available in our Cucumber step implementations. As mentioned, the Geb BindingUpdater adds a browser
variable and multiple methods to be used in the Cucumber step implementation code.
For example, calling browser.go link
can be shortened to:
package steps
import pages.admin.LoginPage
import static cucumber.api.groovy.EN.*
Given(~'^a user opens the browser$') {->
}
When(~'^she opens the "([^"]*)" link$') { String link ->
go link // instead of browser.go link
}
Then(~'^she will get a login page$') {->
}
As the project team is entirely using IntelliJ, I decided to write a GDSL file. GDSLs can be used to add auto-completion support to Groovy DSLs in IntelliJ - exactly what we needed.
Thanks to Peter Gromov from JetBrains I could come up with a GDSL that even has support for generic method type parameters:
/**
* Enables code completion for Geb inside Cucumber step implementations.
*/
def forwardedMethods = [
"go", "to", "via", "at", "waitFor",
"withAlert", "withNoAlert", "withConfirm", "withNoConfirm",
"download", "downloadStream", "downloadText", "downloadBytes", "downloadContent", "report", "reportGroup", "cleanReportGroupDir"]
def scriptContext = context(
filetypes: ['.groovy'],
pathRegexp: ".*/test/cucumber/.*",
scope: closureScope())
contributor(scriptContext) {
property name: 'browser', type: 'geb.Browser'
def methods = findClass('geb.Browser').methods
methods.findAll { it.name in forwardedMethods }.each { def browserMethod ->
def params = [:]
browserMethod.parameterList.parameters.each { param ->
params.put(
param.name,
com.intellij.psi.util.TypeConversionUtil.erasure(param.type).canonicalText)
}
method name: browserMethod.name, type: com.intellij.psi.util.TypeConversionUtil.erasure(browserMethod.returnType).canonicalText, params: params
}
}
The GDSL registers all methods which are bound by the BindingUpdater
plus it adds the browser
variable of type geb.Browser
for all Groovy script files in the test/cucumber/
directory. TypeConversionUtil
is used to type erase the method result and parameter types, so for example <T extends Page> T at(Class<T> pageClass)
will be erased to Page at(Class pageClass)
. One little attack against the DRY principle is the hard-coded list of forwarded methods, that’s something that could be improved with a little IntelliJ PSI kung-fu.
After throwing this as CucumberGebSupport.gdsl
in the test/cucumber
folder (which is being registered as test-source folder), IntelliJ will recognize the file and enable the auto-completion support as specified.
Conclusion
IntelliJ comes with a neat mechanism that allows to add auto-completion support for custom Groovy DSLs. So-called GDSL files can be used to specify delegation classes, introduce variables and do other fancy stuff that needs to be done to have auto-completion for Groovy DSLs.