Unit test Grails GORM’s formulas

The code for this post is part of my PomoTimer project and can be found on GitHub.

The domain class

We have a domain class Project that models a project that one is working on when doing a particular work session. It records the name of the project (“Writing a blog post”), a status (“active”, “completed”), a creation time, the total time spent on this project and the user the project belongs to.

The total time is a derived field: it calculates the time spent on the work sessions that belong to this project. It should only calculate the work sessions that have status “done”. Derived fields can be defined in Grails by so-called formulas.

package com.relentlesscoding.pomotimer

class Project {
    String name
    ProjectStatus status
    Date creationtime
    Integer totaltime
    User user

    static constraints = {
        name blank: false, nullable: false, maxSize: 100, unique: true
        status blank: true, nullable: false, display: true
        creationtime blank: false, nullable: false
        totaltime blank: false, nullable: false, min: 0
        user blank: false, nullable: false, display: true
    }

    static mapping = {
        totaltime formula: '(select ifnull(sum(select d.seconds from Duration d where ws.duration_id = d.id), 0) from Work_Session ws where ws.project_id = id and ws.status_id in (select st.id from Work_Session_Status st where st.name = \'done\'))'
    }

    String toString() { return name }
}

Testing the formula

So how do we test a formula? Initially, I tried to write integration tests (grails create-integration-test <className> that create a class with the grails.testing.mixin.Integration and grails.transaction.Rollback annotations), but these suffer from the limitation that each feature method starts a new transaction, and formula’s aren’t updated until after the transaction is committed (which is never because the transaction is always rolled back). This effectively makes it impossible for an integration test to check whether a formula does what it is supposed to do.

The solution is to write a test specification that extends HibernateSpec, which allows us to fully use Hibernate in our unit tests.

package com.relentlesscoding.pomotimer

import grails.test.hibernate.HibernateSpec

class ProjectTotaltimeSpec extends HibernateSpec {

    def 'adding a work session should increase total time'() {
        given: 'a new project'
        def projectStatus = new ProjectStatus(name: 'foo', description: 'bar')
        def user = new User(firstName: 'foo',
                            lastName: 'bar',
                            userName: 'baz',
                            email: 'q@w.com',
                            password: 'b' * 10)
        def project = new Project(name: 'a new project',
                                  status: projectStatus,
                                  creationtime: new Date(),
                                  totaltime: 0,
                                  user: user)

        expect: 'total time of the new project is 0 seconds'
        project.totaltime == 0

        when: 'adding a completed work session to the new project'
        def duration = new Duration(seconds: 987)
        def done = new WorkSessionStatus(name: 'done', description: 'done')
        new WorkSession(starttime: new Date(),
                        duration: duration,
                        project: project,
                        status: done
                .save(failOnError: true, flush: true)
        // NOTE: a refresh is required to update the domain object
        project.refresh()

        then: 'total time is equal to duration of completed work session'
        project.totaltime == 987
    }
}

How to use Groovy’s CliBuilder

I write quite some scripts at work, either for myself or for my team. I used to write these scripts in Bash, but since we are using Windows at work, it made sense to switch to a more general scripting language. Because we’re a Java shop, Groovy seemed like a natural choice. Now, it’s often very convenient to have named command-line arguments (e.g. tool --output out.ext --input in.ext) as opposed to positional arguments (e.g. tool out.ext in.ext): named arguments allow for putting the arguments in any order you want, and you can leave out optional arguments and use the defaults. Groovy’s CliBuilder makes things especially easy.

A sample program

weather.groovy

@Grab(group='commons-cli', module='commons-cli', version='1.4')

class Main {
    static main(args) {
        CommandLineInterface cli = CommandLineInterface.INSTANCE
        cli.parse(args)
    }
}

enum CommandLineInterface {
    INSTANCE

    CliBuilder cliBuilder

    CommandLineInterface() {
        cliBuilder = new CliBuilder(
                usage: 'weather [<options>]',
                header: 'Options:',
                footer: 'And here we put footer text.'
        )
        // set the amount of columns the usage message will be wide
        cliBuilder.width = 80  // default is 74
        cliBuilder.with {
            h longOpt: 'help', 'Print this help text and exit.'
            n(longOpt: 'max-count', args: 1, argName: 'number',
                    'Limit the number of days shown')
            '1' longOpt: 'one', "Show today's forecast"
            '2' longOpt: 'two', "Show today's and tomorrow's forecast"
            _(longOpt: 'url', args: 1, argName: 'URL',
                    'Use given URL to query for weather.')
            D(args: 2, valueSeparator: '=', argName: 'property=value',
                    'Use value for given property.')
        }
    }

    void parse(args) {
        OptionAccessor options = cliBuilder.parse(args)

        if (!options) {
            System.err << 'Error while parsing command-line options.\n'
            System.exit 1
        }

        if (options.h) {
            cliBuilder.usage()
            System.exit 0
        }

        if (options.n) {
            // handle user's input
        }
        if (options.'1') {
            // show weather for one day
        }
        if (options.url) {
            URI uri = new URI(options.url)
        }
        if (options.Ds) {
            assert options.Ds.class == ArrayList.class
        }
    }
}

When invoked with groovy weather.groovy --help, the program will output:

usage: weather []
Options:
 -1,--one                  Show today's forecast
 -2,--two                  Show today's and tomorrow's forecast
 -D        Use value for given property.
 -h,--help                 Print this help text and exit.
 -n,--max-count    Limit the number of days shown
    --url             Use given URL to query for weather.
And here can we put footer text.

You can specify short and long names: cliBuilder.n specifies that the program will accept an argument -n. To also specify a long name, we add longOpt: max-count. To use only a long name, we can replace the short name by _: cliBuilder._(longName: 'wow-such-long'). Note that cliBuilder._ can be reused several times (in contrast to other short names).

The number of arguments can be indicated by using the args parameter. If you use more than one parameter, a valueSeparator must be indicated, which is used to split up the argument. I found that the number assigned to args is not very strict: when called with argument -Dmyprop, the following is valid.

assert options.Ds == ['myprop']

Note how we append an s to the option name (options.Ds) to get a list of properties and values. A normal invocation groovy weather.groovy -Dpancakes=yesplease would result in:

assert options.Ds == ['pancakes', 'yesplease']

When required: true is set, the program will shout at you if the argument is not used. For example, if we specify cliBuilder.q(required: true) but fail to provide the -q argument on the command line, the program will exit with error: Missing required option: q.

One thing to remember is that, if you want to use digits as argument names, you have to stringify them first by putting quotation marks around them: cliBuilder.'1'.

More on CliBuilder

Check the Groovy documentation on CliBuilder for more .

Dependencies

Groovy’s CliBuilder depends on cli-commons:

build.gradle

dependencies {
    compile group: 'commons-cli', name: 'commons-cli', version: '1.4'
}

Using Groovy’s AntBuilder to zip and unzip files

Suppose we need to zip a bunch of Python files (without their compiled counterparts *.pyc). The next (admittedly contrived) example shows how we could go about doing something like this in Groovy:

File srcDir = new File('/home/neftas/Downloads/scripts/')
File destDir = new File(srcDir, 'antexample')
File zippedFile = new File(srcDir, 'antzipped.zip')

def allPythonFilenames = srcDir.list().sort().findAll { it.endsWith('.py') }
assert ['a.py', 'b.py', 'c.py'] == allPythonFilenames

def ant = new AntBuilder()

ant.with {
    echo 'begin zipping and unzipping'
    zip(destfile: zippedFile, basedir: srcDir, includes: '*.py')
    mkdir(dir: destDir)
    unzip(src: zippedFile, dest: destDir)
    echo 'done zipping and unzipping'
}

// zip file is created
assert zippedFile.exists()
// contents of zip file should match content of source directory
def commands = ['bash', '-c', "zipinfo -1 ${zippedFile.name}"]
def process = commands.execute(null, srcDir)
def contentsOfZip = process.text.split(System.lineSeparator)
assert contentsOfZip.sort() == allPythonFilenames

// all files should be unpacked from the zip into the right directory
assert destDir.list().sort() == allPythonFilenames

ant.with {
    echo 'deleting created files'
    // notice nested Ant task
    delete(includeEmptyDirs: true) {
        fileset(dir: destDir)
        fileset(file: zippedFile)
    }
    echo 'deleted created files'
}

We can use all the power of Ant in our Groovy scripts. Ant task names map to Groovy methods (see the zip method above) and the attributes are passed as maps to these methods. What’s even better, there is no need to stringify the arguments of these attributes (as is required in Ant’s XML), but we can directly use the correct datatypes (e.g. in the key-value pair destfile: zippedFile, zippedFile is of type java.util.File). All attributes of Ant tasks are available to use with AntBuilder (see Ant manual for zip task).

To use nested Ant tasks, we create closures, so that Ant’s:

<delete includeEmptyDirs="true">
    <fileset dir="lib">
    <fileset file="file.ext">
</delete>

results in the following Groovy code:

ant.delete(includeEmptyDirs: true) {
    fileset(dir: 'lib')
    fileset(file: 'file.ext')
}

Invoking from Gradle

AntBuilder is also available in Gradle. This will come in handy when you don’t know the "Gradle way" of doing something, but do know how to tackle it in Ant.

Dependencies

Groovy (and Gradle) come with a bundled version of AntBuilder, so you should be good shape when creating a Groovy script, but if you want to use AntBuilder in a project, you should add ant and ant-launcher to your build.gradle:

dependencies {
    compile group: 'ant', name: 'ant',  version: '1.7.0'
    compile group: 'ant', name: 'ant-launcher',  version: '1.6.5'
}

I like my method pointers with curry in Groovy

Invoking a method several times can easily be done in Java in a loop. But what if we have several methods that need to be executed several times? Since Java does not feature pointers to functions, we cannot pass functions around, and cannot create a generic method that takes a pointer to a method and executes it. This is where Groovy shines.

Suppose we want to check on the status of some external system or work. We want to check the status every half a second for a maximum of ten times in total.

// the generic type in Closure<Boolean> indicates its return type
boolean isOperationSuccessful(int numOfTries, Closure<Boolean> action) {
    Thread.sleep(500)
    for (i in 0..<numOfTries) {
        if (action()) {
            return true
        } else {
            Thread.sleep(500)
        }
    }
}

// we can pass closures...
def check = { int n -> new Random().nextInt(10) == n }
println isOperationSuccessful(10, check.curry(3))

// ... as well as methods
boolean isWorkDone(int id) {
    // check if particular external system identified by `id` is done
    return new Random().nextInt(2) == 1
}
// `.&` is the method pointer operator
println isOperationSuccessful(5, this.&isWorkDone.curry(1))

We can pass methods around by referencing them with the method pointer operator .&:

def pointerToUpper = 'hello world'.&toUpperCase
assert pointerToUpper() == 'HELLO WORLD'

By using Groovy’s take on currying, we can fix parameters of a closure (or method):

def power(double n, int exponent) { n ** exponent }
def squared = this.&power.rcurry(2)
assert squared(12) == 144

rcurry fixes the right-most argument of the method, curry the left-most. (And yes, there is also an index-based method: ncurry). Both return a new Closure that takes one less argument.