Marcin Erdmann

Groovy, Grails, Geb...

How to fix IncompatibleClassChangeError for your Groovy projects running on JDK7

Shortly after JDK7 was released people started complaining on Geb's user mailing list about problems with running Geb on JDK7. The stacktrace contained a line simillar to this one:

Caused by: java.lang.IncompatibleClassChangeError: the number of constructors during runtime and compile time for java.lang.RuntimeException do not match. Expected 4 but got 5

Luke Daley has found out that it is due to the fact that Groovy inspects the number of constructors on classes during compilation and checks if it is still the same during runtime. JDK7 has added a fifth constructor to Exception class so if you compiled your Groovy code that contains any exceptions written in Groovy with JDK6 and you tried to run it with JDK7 you would get a java.lang.IncompatibleClassChangeError.

There is a simple solution to that issue - all you have to do is rewrite your exceptions from Groovy to Java, which will soon remind you how cool Groovy is and how bloated Java code seems to be after not writing any of it for a while.

Just to clarify it's not me who came up with that idea - all credit goes to Luke. I just wanted to write about it because after we fixed it in Geb the issue stroke again... Luke compiled the latest version of Groovy Remote Control with JDK7 and I used it as a dependency in a JDK6 based project. The good thing is that I knew what was going on straightaway and simply rewrote the exception classes in Groovy Remote Control and the next release of the library will be compatible with both JDK6 and JDK7. Also other people hitting the issue might find this post helpful...

Built in way of mocking g.render() in Grails 2

In my previous post I've came up with a way of mocking g.render() tag in Grails 2 unit tests. As Rob Fletcher has pointed out to me there is a much simpler, built in way of doing that.

Looks like I haven't paid enough attention while looking at ControllerUnitTestMixin, a superclass of GroovyPageUnitTestMixin which is being applied to your taglib tests when marking them with @TestFor. Apparently there is a views field that allows you to specify templates without using the file system. Using that field the test for the handlebars template rendering taglib from the earlier post becomes much simpler:

@TestFor(HandlebarsTagLib)
class HandlebarsTagLibSpec extends Specification {

    def 'jsTemplate renders script tag with template'() {
        given:
        def templateName = 'someTemplate'
        views["/handlebars/_${templateName}.gsp"] = '<div>template content</div>'

        when:
        String rendered = applyTemplate("<handlebars:template name=\"${templateName}\" />")
        GPathResult result = new XmlSlurper().parseText(rendered)

        then:
        result.name() == 'script'
        result.@type == HANDLEBARS_TAGLIB_MEDIA_TYPE
        result.@id == templateName
        result.div.text() == 'template content'
        }

    def 'exception is thrown when js template is not found'() {
        given:
        def templateName = 'doesNotExist'

        when:
        applyTemplate("<handlebars:template name=\"${templateName}\" />")

        then:
        GrailsTagException exception = thrown()
        exception.message.startsWith("Template not found for name [/handlebars/${templateName}]")
    }
}

Mocking g.render() tag in Grails 2

With the addition of @TestFor and test mixins unit testing of taglibs has been completely changed in Grails 2. One of things that changed is testing custom taglibs that use default Grails tags, for example g.render(). Beforehand your tests would throw errors if you haven't mocked it, but now a call to g.render() will behave as if in a running application - pick up the specified template from grails-app/views and render it. Sometimes it is not what you really want.

Let's say that we are creating a taglib to render handlebars templates. Given the following template located in grails-app/views/handlebars/myTemplate.gsp:

<h1>{{title}}</h1>

and a following taglib call in our view:

<handlebars:template name="myTemplate" />

based on conventions we expect the following result:

<script id="myTemplate" type="text/x-handlebars-template">
    <h1>{{title}}</h1>
</script>

Our taglib will simply render the template from grails-app/views/handlebars with a given name and wrap it with a script tag of a hanldebars template specific type. But how do we test it? We don't want to add a template to grails-app/views/handlebars just for test purposes.


UPDATE: You should probably ignore the following and see the follow up post as Rob Fletcher has pointed out to me a much simpler way of doing it.

After having a quite lengthy look at implementation of g.render() I've noticed that it uses GrailsConventionGroovyPageLocator#findTemplateInBinding() which returns a GroovyPageScriptSource instance to load scripts. The locator used comes from groovyPageLocator field of GroovyPagesTemplateRenderer bean. Knowing that we can now easily mock the script locator in our Spock specification:

@TestFor(HandlebarsTagLib)
class HandlebarsTagLibSpec extends Specification {

    GrailsConventionGroovyPageLocator mockScriptLocator
    GroovyPageScriptSource mockScript

    def setup() {
        mockScriptLocator = Mock(GrailsConventionGroovyPageLocator)
        mockScript = Mock(GroovyPageScriptSource)

        GroovyPagesTemplateRenderer renderer = applicationContext.getBean(GroovyPagesTemplateRenderer)
        renderer.groovyPageLocator = mockScriptLocator
    }

    private void mockGspTemplateContents(String path, String contents) {
        mockScript.URI >> new URI(new Date().time.toString())
        mockScript.suggestedClassName() >> 'pageName'
        mockScript.scriptAsString >> contents
        mockScriptLocator.findTemplateInBinding(_, path, _) >> mockScript
    }

    private void mockGspTemplateMissing(String path) {
        mockScriptLocator.findTemplateInBinding(_, path, _) >> null
    }
}

There are some calls to getURI() and suggestedClassName() on the script that also have to be mocked for the technique to work. Note that we are just returning dummy value for page name as otherwise we end up with a NullPointerException and we want the script URI to be unique as otherwise it might get pulled from a cache.

Now we can write our features - one for an existing template and one for a missing template:

@TestFor(HandlebarsTagLib)
class HandlebarsTagLibSpec extends Specification {

    (...)

    def 'jsTemplate renders script tag with template'() {
        given:
        def templateName = 'someTemplate'
        mockGspTemplateContents("/handlebars/${templateName}", '<div>template content</div>')

        when:
        String rendered = applyTemplate("<handlebars:template name=\"${templateName}\" />")
        GPathResult result = new XmlSlurper().parseText(rendered)

        then:
        result.name() == 'script'
        result.@type == HANDLEBARS_TAGLIB_MEDIA_TYPE
        result.@id == templateName
        result.div.text() == 'template content'
    }

    def 'exception is thrown when js template is not found'() {
        given:
        def templateName = 'doesNotExist'
        def templatePath = "/handlebars/${templateName}"

        mockGspTemplateMissing(templatePath)

        when:
        applyTemplate("<handlebars:template name=\"${templateName}\" />")

        then:
        GrailsTagException exception = thrown()
        exception.message.startsWith("Template not found for name [${templatePath}]")
    }
}

Having the tests in place we can now write our implementation:

class HandlebarsTagLib {
    static namespace = 'handlebars'

    static final HANDLEBARS_TAGLIB_MEDIA_TYPE = 'text/x-handlebars-template'

    def template = { attrs, body ->
        new MarkupBuilder(out).script(type: HANDLEBARS_TAGLIB_MEDIA_TYPE, id: attrs.name) {
            mkp.yieldUnescaped(g.render(template: "/handlebars/${attrs.name}"))
        }
    }
}

Gradle GAE-Geb plugin released!

Thanks to the release of gradle-gae-geb-plugin you no longer have to specify baseUrl in GebConfig.groovy when testing Gae(lyk) applications with Geb. What's more the plugin determines baseUrl based on the configuration of gradle-gae-plugin in your build.gradle - so if you configure your dev server to run on a different port than 8080 it will be picked up.

All you have to do is simply apply the plugin in your gradle build file:

apply plugin: 'gae-geb'

buildscript {
    repositories {
        mavenCentral()
    }

    dependencies {
        classpath 'org.gradle.api.plugins:gradle-gae-geb-plugin:0.2'
    }
}

Please stay tuned as there are more improvements coming up in the area of functional testing of Gaelyk applications.

Functional testing of Gae(lyk) applications with Geb

Thanks to the addition of gaeFunctionalTest task in version 0.7 of gradle-gae-plugin it is now extremely easy to set up Geb functional tests for GAE and Gaelyk applications. All of the following will soon be a part of the Gaelyk template project (when 1.2 is released) but for plain GAE applications following are the steps to achieve it:

  1. Apply Groovy plugin to your project as you will be writing your tests (specifications to be precise) in Groovy using Spock.

    apply plugin: 'groovy'
    
  2. Add depenendencies on Groovy, Spock, Geb and a WebDriver implementation (for example for Firefox).

    dependencies {
        groovy 'org.codehaus.groovy:groovy-all:1.8.6'
        functionalTestCompile 'org.spockframework:spock-core:0.6-groovy-1.8', {
                exclude group: 'org.codehaus.groovy', module: 'groovy-all'
            }
        functionalTestCompile 'org.codehaus.geb:geb-spock:0.7.0',
            'org.seleniumhq.selenium:selenium-firefox-driver:2.20.0'
    }
    
  3. Create src/functionalTest/groovy directory and put your specifications in it (see Geb docs for examples).

  4. Apply gradle-gae-geb-plugin so that Geb's baseUrl configuration property is set based on the gradle-gae-plugin conventions of your build:

    apply plugin: 'gae-geb'
    
    
    buildscript {
        repositories {
            mavenCentral()
        }
        dependencies {
            classpath 'org.gradle.api.plugins:gradle-gae-geb-plugin:0.2'
        }
    }
    

Now you can simply run your functional tests by executing:

gradlew gaeFunctionalTest

Two more things to note:

  • check task now depends on gaeFunctionalTest so if you want to run all of your tests (both functional and unit) you can simply call gradle check. It also means that when running gradle build your functional tests will be executed as well.
  • all of your new your dependencies and new source set will be added to your IntelliJ project if only you run gradle idea command. Thanks to that you can easily run the tests directly from your IDE - simply start the development server first (gradle gaeRun) and then execute the tests.