Wednesday, February 10, 2010

Stub static method on domain class in Grails explained

Since Grails 1.1 the unit test capability has been greatly improved for pretty much anything you can imagine in Grails from domain class to controller even taglibs can be easily tested in plain old junit with the help of GrailsUnitTestCase and many of it's cousins such as ControllerUnitTestCase specialized in different aspects of the framework.

However the fantastic mocking framework is not without its boundary. The mockDomain method provided by GrailsUnitTestCase has its limit, it does not provide the capability to fully mock GORM dynamic finders as well as criteria and executeQuery methods. Luckily the test framework also provide the ability to mock any method regular or static using mockFor() method (details) however it comes short when you have static query method created on domain class. Imagine the following example:


class Foo{
...

static def listAllAwesomeFoo(keyword, params) {
Foo.executeQuery('''select f from Foo as f
inner join f.tags as t
where f.awesome = true and t.keyword = ?''',
[keyword],
params)
}
}

class FooService {
def doSomethingUseful(){
def awesomeFoos = Foo.listAllAwesomeFoo('something', [max:10])
....
}
}


Now to test the FooService you need the ability to stub or mock the Foo.listAllAwesomeFoo() method and since its a static method there is really no easy way to do it even with Grails test framework; or is there? This is where Groovy MOP can save you a lot of grief and time. To stub this kind of static method all you need to do in Groovy is add the following code before your test code:


Foo.metaClass.static.listAllAwesomeFoo = {keyword, params -> [foo1, foo2] }


Thats it. Now you can happily test all your service logic without worrying about stubbing the whole database to run the HQL. Later on you can always isolate and use integration test with in-memory database to test the HQL separately.

Wednesday, February 03, 2010

Create Open Flash Chart in Grails without using plugins

If you are like me tired of outdated Grails plugins and verbose API in Java for Open Flash Chart 2, here is a short demonstration of generating OFC2 using just naked Grooviness. OFC2 charts are driven by JSON data feed, and with Grails' built in JSON converter and support you really don't need another API or plugin to use OFC2. Just download the flash binary and javascript from http://teethgrinder.co.uk/open-flash-chart-2/ and create a controller in your Grails application. Now you have two choices, you can either use the JSON builder in your controller or just plain maps to generate JSON data. I chose plain maps since its easier to manipulate and examine in unit tests, but the JSON builder will work just as well with even more expressive DSL. Here is an example of a simple chart:


def chartData = [
"elements": [ ["type": "line", "values": [ 1, 2, 1] ] ],
"title": [ "text": "Wed Feb 03 2010" ]
]
render chartData as JSON


Now you are free to use the latest OFC binary without worrying about upgrading the plugin or mocking the complex API in your test. The syntax is extremely similar to the JSON output so you can basically learn from checking out the examples on OFC site. Here is a more complicated example with tooltips, on-load animation and custom look-and-feel.


[
'elements': [
["font-size": 10, "text": "Daily Info",
"tip": '$#val#
on #x_label#', "type": "line", "width": 4,
"dot-style": ["type": "dot", "dot-size": 5, "colour": "#DFC329", "tip": '$#val#
on #x_label#'],
'values': values, 'colour': '#8F9CFF', "on-show": ["type": "drop", "delay": 0.5]
]
],
"is_decimal_separator_comma": 0,
"is_fixed_num_decimals_forced": 0,
"is_thousand_separator_disabled": 0,
"num_decimals": 2,
'title': ['text': "Sample Chart"],
'x_axis': ['steps': 10, labels: ['steps':10, 'visible-steps': 5, 'labels': labels], "colour": "#0067A6", "grid-colour": "#FFCAA8"],
'y_axis': ['min': 0, 'max': yMax, 'steps': calculateYAxisSteps(yMax), "colour": "#0067A6", "grid-colour": "#DEFFA8"],
'bg_colour': '#FFFFFF',
"tooltip": [
"colour": "#5CC0FF", "background": "#FFFFFF", "title": "{font-size: 14px; color: #5C6FFF; font-weight: bold}",
"body": "{font-size: 11px; font-weight: bold; color: #000000;}"
]
]