Fork me on GitHub

17 Jan 2010

A Gradle build for Grails plugins with test apps

There's a fairly popular technique in Grails plugin development where the plugin has 'test apps' stored in test/projects/*, each of which references the plugin by including grails.plugin.location."my-plugin" = "../../.." in its BuildConfig.groovy. Doing this allows you as a plugin developer to:
  • Automate tests for sample projects using the plugin with significantly different configurations.
  • Test using domain classes, controllers, etc. that shouldn't be packaged with the plugin.
  • Test drive, changing code in both the test app and the plugin without the need to use grails package-plugin or grails install-plugin to pick up changes.
The downside is that to run all the plugin project's tests it's necessary to run the plugin's own tests, then change directory into each of the test apps and run their tests. Continuous integration config is also fiddlier for the same reason.

I wanted to find a way to automate this so I could run all my plugins' tests with a single command. I could write a bash script or an Ant build I guess, or even use maven <shudder>. However keeping things in Groovy-land I decided to try using Gradle which I've been meaning to look into for a while now. I saw Hans Dockter's presentation on Gradle at the London Groovy & Grails Exchange back in December and was impressed with how terse and expressive the syntax is, especially compared to XML based formats. Unfortunately one thing Gradle doesn't grok yet is Grails.

The solution I've come up with is based on a post by Helmut Denk on the gradle-user list. Gradle just uses Ant's exec task to shell out and execute the appropriate Grails command. Combining this with Gradle's multi-project build support I now have plugin builds that can be run with gradle test that will automatically descend into the test apps under test/projects/*.

The build.gradle file at the root of the project defines clean and test tasks: The test task will run the plugin's unit tests. The clean task is defined just once for all projects as there's no difference in how it is done - a nice examply of Gradle's DRY-ness.

Then in settings.gradle I tell Gradle where to find the various sub-projects:
Finally each test app has it's own build.gradle that defines its test task: This is only necessary if the test apps run tests differently to the plugin - here my app is running integration rather than unit tests - otherwise I could have defined test in the same DRY manner as clean.

The process is not as fast as it could be if Grails were wired in to Gradle properly. gradle clean test for the Springcache plugin and its two test apps takes just over 2 minutes on my MBP. Also, my Gradle-fu leaves a lot to be desired right now so I'm sure there are improvements that could be made with the way I'm handling the sub-projects. But, for the purposes of a pre-commit test run or Hudson build this works pretty well.

4 Jan 2010

Upgrading Grails 1.1 -> 1.2

We've just successfully upgraded our app from Grails 1.1 to 1.2 and pushed it to our QA environment. I thought I'd write up some of the issues we encountered in case anyone is bashing their heads against them.


Custom constraints that execute queries

Grails 1.2 now executes domain object validation on any unsaved objects when the Hibernate session flushes. One upshot of this is that if you have a custom constraint that executes a query you can end up with a StackOverflowError; the query causes the session to flush, causing the object to be validated, causing the query to run, causing the session to flush, etc.

The solution is to use one of the new domain class dynamic methods withNewSession to execute the query in a separate Hibernate session.

For example, we have a domain class where only a single instance can exist in a 'live' state. A simple unique constraint won't work as any number of instances can exist in other states. The final constraint looks something like this:

static constraints = {
    state validator: { value, self ->
        boolean valid = true
        if (value == State.LIVE) {
            Homepage.withNewSession {
                valid = Homepage.countByStateAndIdNotEqual(State.LIVE, self.id) == 0
            }
        }
        return valid
    }
}

Errors set outside constraints

The new validation on flush behaviour also caused us a more subtle and difficult to trace problem. One of our controllers checks that a date/time property on a domain class instance is in the future when the instance is saved. Doing this in a constraint isn't desirable as it makes it awkward to set up test data or re-save instances in other contexts - the rule really only applies to the domain class when the user creates or updates it on the one particular form.

The validation code in the controller looks something like this:

domainInstance.validate()
if (domainInstance.goLiveTime < new Date()) {
    domainInstance.rejectValue "goLiveTime", "min.notMet"
}
if (!domainInstance.hasErrors() && domainInstance.save()) {
    // redirect
} else {
    // re-render form with errors
}

When upgrading one of our Selenium tests started failing as, although the save was failing, the error message did not get rendered on the form. What was happening is that at the end of the controller action the entity was not saved as it had errors but Hibernate attempted to flush and triggered re-validation which wiped out the error the controller had set.

If save had been called and validation failed then the object would have been made read-only so Hibernate would not attempt to flush it. My solution was to do this explicitly in the else block before rendering the form using GrailsHibernateUtil.setObjectToReadOnly(domainInstance, sessionFactory). It's certainly not ideal that the controller should be aware of this kind of low-level detail so some refactoring is in order here, but it solved the problem.

Specifying fetch modes on associations in criteria queries

We have one particularly nasty query that retrieves a domain object instance and loads an entire hierarchy of associations using several fetchMode "property", FetchMode.JOIN in the criteria query. For example if Author has many books and each Book has many chapters the query previously looked like:

Author.withCriteria {
    // some criteria
    fetchMode "books", FetchMode.JOIN
    fetchMode "chapters", FetchMode.JOIN
}

This always seemed syntactically odd as it appears that chapters is a property of Author when it is in fact a property of each member of Author.books. In Grails 1.2 the syntax makes much more sense as you nest the fetchMode declarations just as you would other criteria. So the example above would become:

Author.withCriteria {
    // some criteria
    fetchMode "books", FetchMode.JOIN
    books {
        fetchMode "chapters", FetchMode.JOIN
    }
}

Date binding format

Date binding now uses a fixed format of yyyy-MM-dd HH:mm:ss.S rather than the short format of the request locale. We have a couple of forms where dates are entered as text rather than using a date picker or rich control and the users (and our tests) expect to be able to enter dates as dd/MM/yy. In order to keep this functionality working I had to create a class that implements PropertyEditorRegistrar and deploy it in grails-app/conf/spring/resources.groovy. The class registers a custom date editor using the required format.

Because the custom property editor registration happens on a per-request basis it would be simple to use the same technique to allow users to enter dates in the locale format they are used to (Americans with their crazy month-day-year format and everyone else with something sensible, for example).

Setting String values in g:set tags

One of the main features of Grails 1.2 is the enhanced GSP rendering performance. One way this was achieved as I understand it is that mutable streams are used rather than immutable String instances where possible. One minor change I had to make to support this was changing g:set tags in the format <g:set var="name">${value}</g:set> to <g:set var="name" value="${value}"/> Variable created in the first manner are now instances of StreamCharBuffer rather than java.lang.String which sometimes caused problems when they were used later.

Flash scope and null values

The flash scope object available to controllers is now an instance of ConcurrentHashMap which means none of its keys can map to null. There were a few places where we were setting flash.message to the result of a method call that might return null. I simply ensured the empty string was used instead.

Ultimately the upgrade took a lot less time and effort than when we went from Grails 1.0.3 to 1.1 and we thankfully didn't encounter any blocking issues. The impression I have is that the platform's really maturing nicely. Will be cool to start using some of the new features of 1.2 in the next few weeks.

2 Jan 2010

Selenium RC tests with a running Grails app

I posted recently about the new remote mode feature of the Selenium RC plugin. One thing I forgot to mention is that this feature can also be used to run your Selenium tests interactively against a running app instance. You can start your app as normal using grails run-app then either open a second terminal or background the process (Ctrl-Z in a bash shell) then use grails -Dselenium.remote=true test-app other:selenium <test name> to run individual tests without stopping or re-starting the app. With the app running development mode you can effectively test-drive using Selenium tests and Grails' artefact reloading capabilities.

One thing I should clarify is that direct domain class access will not work in remote mode. I'm thinking about ways to add fixtures support to the Selenium RC plugin so there's a good alternative approach that will work in remote mode.

1 Jan 2010

Using GMock to complement Grails mockDomain

Since Grails 1.1 we've had pretty good unit testing support via GrailsUnitTestCase and its sub-classes. The mockDomain method is particularly useful for simulating the various enhancements Grails adds to domain classes. However, there are some domain class capabilities, such as criteria queries and the new named queries, that can't really be simulated by mockDomain.

So assuming we're trying to unit test a controller that uses criteria methods or named queries on a domain class how can we enhance the capabilities of mockDomain? One of my favourite Groovy libraries is GMock which I use in preference to Groovy's built in mock capabilities. One of its really powerful features is the ability to use 'partial mocks', i.e. to mock particular methods on a class whilst allowing the rest of the class to continue functioning as normal. This means we can layer a mocked createCriteria, withCriteria or named query call on to a domain class that is already enhanced by mockDomain.

First off you need to add the GMock dependency to your BuildConfig.groovy. Since GMock supports Hamcrest matchers for matching method arguments you'll probably want those as well:
    dependencies {
        test "org.gmock:gmock:0.8.0"
        test "org.hamcrest:hamcrest-all:1.0"
    }
If you're using an earlier version of Grails you'll need to just grab the jar files and put them in your app's lib directory.

Then in your test case you need to import GMock and Hamcrest classes and add an annotation to allow GMock to work:
    import grails.test.*
    import org.gmock.*
    import static org.hamcrest.Matchers.*

    @WithGMock
    class MyControllerTests extends ControllerUnitTestCase {

Adding criteria and named query methods is now fairly simple:

Mocking a withCriteria method

    def results = // whatever you want your criteria query to return
    mock(MyDomain).static.withCriteria(instanceOf(Closure)).returns(results)
    play {
        controller.myAction()
    }
Breaking this example down a little
  1. mock(MyDomain) establishes a partial mock of the domain class.
  2. instanceOf(Closure) uses a Hamcrest instanceOf matcher to assert that the withCriteria method is called with a single Closure argument (the bit with all the criteria in).
  3. returns(results) tells the mock to return the specified results which here would be a list of domain object instances.
In this example we're expecting the withCriteria method to be called just once but GMock supports more complex time matching expressions if the method may be called again.

Mocking a createCriteria method

The withCriteria method returns results directly but createCriteria is a little more complicated in that it returns a criteria object that has methods such as list, count and get. To simulate this we'll need to have the mocked createCriteria method return a mocked criteria object.
    def results = // whatever you want your criteria query to return
    def mockCriteria = mock() {
        list(instanceOf(Closure)).returns(results)
    }
    mock(MyDomain).static.createCriteria().returns(mockCriteria)
    play {
        controller.myAction()
    }
This is only a little more complex than the previous example in that it has the mocked list method on another mock object that is returned by the domain class' createCriteria method. mock() provides an un-typed mock object as we really don't care about the type here.

Mocking a named query

For our purposes named queries are actually pretty similar to createCriteria.
    def results = // whatever you want your criteria query to return
    def mockCriteria = mock() {
        list(instanceOf(Closure)).returns(results)
    }
    mock(MyDomain).static.myNamedQuery().returns(mockCriteria)
    play {
        controller.myAction()
    }

Some other examples:

Mocking a named query with an argument

    mock(MyDomain).static.myNamedQuery("blah").returns(mockCriteria)
For simple parameters you don't need to use a Hamcrest matcher - a literal is just fine.

Mocking a withCriteria call using options

You can pass an argument map to withCriteria, e.g. withCriteria(uniqueResult: true) { /* criteria */ } will return a single instance rather than a List. To mock this you will need to expect the Map as well as the Closure:
    def result = // a single domain object instance
    mock(MyDomain).static.withCriteria(uniqueResult: true, instanceOf(Closure)).returns(result)

Mocking criteria that are re-used

It's fairly common in pagination scenarios to call a list and count method on a criteria object. We can just set multiple expectations on the mock criteria object, e.g.
    def mockCriteria = mock() {
        list(max: 10).returns(results)
        count().returns(999)
    }
    mock(MyDomain).static.myNamedQuery().returns(mockCriteria)

The nice thing about this technique is that it doesn't interfere with any of the enhancements mockDomain makes to the domain class, so the save, validate, etc. methods will still work as will dynamic finders.

Be aware however, that what we're doing here is mocking out the criteria queries, not testing them! All the interesting stuff inside the criteria closure is being ignored by the mocks and could, of course, be garbage. Named queries are pretty easy to test by having integration test cases for your domain class. Criteria queries beyond a trivial level of complexity should really be encapsulated in service methods or named queries and integration tested there. Of course, GMock then makes an excellent solution for mocking that service method in your controller unit test.