Friday, January 7, 2011

Groovy/Grails DSL testing

Today I faced a usual task of testing a controller action that utilizes the withCriteria call to fetch some data from the database. Usually what I do in situation like this is I write an integration test, have the database mocked using in-memory instance of HSQLDb and all is nice and dandy.

This time I didn't have the luxury to have the database in place. I will not bore you why that is the case - it simply is. What I didn't want to do was to just let the thing be untested so I've decided to create a ultra-minimalistic framework for testing such DSLs in Groovy.

First of all, it all bases on the assumption that you don't care about the actual results the DSL brings. What you do care about is the fact that certain parts of the DSL have been called in a particular order and with proper arguments. To put this in context: you don't care that the withCriteria DSL will be in reality transformed into SQL but you do care that what you wrote will be as you expect it to be.

So the following class has emerged:
package groovy.test

class DSLTester {
static mock(clazz, method, result) {
def tester = new DSLTester()
clazz.metaClass.static."${method}" = { Closure dsl ->
dsl.delegate = tester
dsl.resolveStrategy = Closure.DELEGATE_FIRST
dsl()
return result
}
return tester
}

def result = []

def methodMissing(String name, args) {
result << [ name: name, args: args.toList() ]
}
}

What it does is it executes the closure passed on as the DSL to whatever static method we decide to test and catches all the missing calls as if they were proper DSL calls. You might find the args.toList() part to be a little bit strange. This is to allow use simple comparison in tests - read on to find how.

To give you an example first let's define the Person class:
class Person {
String firstName
String lastName
}

With that in hand let's create a home controller that will just print all the people ordered by firstName:
class HomeController {
def index = {
def people = Person.withCriteria {
order "lastName"
}

[ people: people ]
}
}

To test the withCriteria closure let's utilize the DSLTester class:
import groovy.test.DSLTester
import grails.test.ControllerUnitTestCase
import org.junit.Test

class HomeControllerTests extends ControllerUnitTestCase {
void testExecuteDSL() {
def mock = DSLTester.mock(Person, 'withCriteria', [])
def actual = controller.index()
assert mock.result == [ [ name: 'order', args: [ 'lastName' ] ] ]
}
}

As you can see the only assertion that I make here is that the order will be properly set to 'lastName' which is what we wanted in the first place.

There's tons of ways you could improve this. You could for example come up with a nice way to verify expected methods have been called with the proper arguments instead of declaring a huge list with hashes containing everything. Sky is the limit :)

The main motivation behind this sort of test is to provide means to test only your code and to test it in complete isolation. It might be perfectly understandable that since the code that uses withCriteria has such a strong dependency on the database that testing it in isolation makes no sense. I however like to keep things isolated :)

1 comment:

uchiha manga said...

Merci pour logiciel et pour les informations


Si tu veux telecharger des logiciels gratuit free ICIIII


Et pour telecharger la meilleure version de logiciel test adsl