Thursday 23 September 2010

Stubbing g.message

It's easy to mock grails taglibs using gmock and hamcrest. You can do something like...

...
def g

void setup() {
g = mock()
mock(tagLib).getG().returns(g).stub()
}

void someTest() {
g.message(hasEntry('code', 'foo.bar')).returns('FOO BAR')
String result
play {
result = tagLib.someMethod([:]).toString()
}
assert result.contains('FOO BAR')
}
}

But when you've got a lot of calls to g.message this can become noisy and usually ends up being repeated in multiple tests. One option is to be relax the argument matching and switch to using a stub.

g.message(instanceOf(Map)).returns('FOO BAR').stub()

This is fine for the tests where you don't really care about the message, but too loose for the ones you do. I'm playing with the an alternative which gives you both stubbing and the option of explicitly asserting g.message was called with the correct arguments. Be warned though it may raise a WTF exception on first glance.

...
def g

void setup() {
g = mock()
mock(tagLib).getG().returns(g).stub()
stubMessages(g)
}

void someTest() {
String result
play {
result = tagLib.someMethod([:]).toString()
}
assert result.contains('code:foo.bar')
}
}

class MessageMatcher extends BaseMatcher {

String code = ''

static void stubMessages(def g) {
MessageMatcher matcher = new MessageMatcher()
g.message(matcher).returns(matcher).stub()
}

boolean matches(Object o) {
code = ((Map) o).containsKey('code') ? o.code : ''
return true;
}

void describeTo(Description description) { }

String toString() {
return "code:${code}"
}
}

The tricky bit was getting the g.message stub to return a value derived from the matcher arguments. I couldn't just do

g.message(matcher).returns(matcher.code).stub()

because matcher.code won't have a value at this point. The solution is to override the matcher's toString() method to return the code and rely on groovy / grails invoking toString() when adding the matcher to the output. This certainly violates the rule of least surprises, but I think I'm OK with that if it reduces duplication and the noise level of my tests - at least until I find a better way.

2 comments:

Rob said...
This comment has been removed by the author.
Rob said...

I had a think about this and came up with an alternate approach: http://adhockery.blogspot.com/2010/09/stubbing-access-to-gmessage-tag-in-unit.html