Unit testing grails tag libs

Suppose you want to write your own tag library in grails. You put the tag lib class in grails-app/taglib. You put the test for the tag lib in test/unit. But how do you write the unit test?

You let your test class extend TagLibUnitTestCase. But that doesn’t solve all problems. We wrote a tag lib for internationalising error messages. And we ran into two problems:

  1. The tag lib writes to an output stream. We want to intercept what is written to the output stream. But how do we create a mock for the output stream?
  2. Our tag lib reads messages from a message source. Within the tag lib class, the message source is retrieved from a grailsAttributes property. It seems we have to replace this property by a mock. But how?

Intercepting the output

The answer to the first question isn’t all that difficult. The tag lib has a property out that contains the output stream. We can simply replace it, by using meta object programming. We set up the cut (class under test) in our setUp() method. Do not forget the call to super.setUp(), because otherwise your test won’t work.

class I18nErrorsTagLibUnitTests extends TagLibUnitTestCase {

  private I18nErrorsTagLib cut
  private StringWriter output

  protected void setUp() {
    cut = new I18nErrorsTagLib()
    output = new StringWriter()
    cut.metaClass.out = output

We can now simply test if the correct output is written, by making assertions on the contents of the output object:

  void testSimpleMessage() {
    // call a method on the tag lib
    cut.message(error: errorObject)
    // check the output
    assertEquals "<output written by tag lib>", output.toString()

Injecting the message source

Replacing the object’s message source by our mock object follows the same principle. But there are a few more steps involved. To get the message source, the tag lib does the following:

  1. The tag lib gets the value from its grailsAttributes property.
  2. The grailsAttributes object has a function getApplicationContext().
  3. On the application context, the bean with name “messageSource” is retrieved.

In other words, the tag lib uses the following code to get the message source:

  def messageSource = grailsAttributes.getApplicationContext().getBean("messageSource")

How do we mock this?

  1. First, we mock the grailsAttributes property, by overriding the metaClass’ getGrailsAttributes() method. (Remember that accessing the property grailsAttributes is the same as calling the method getGrailsAttributes()).
      cut.metaClass.getGrailsAttributes = { getGrailsAttributesStub() }
  2. Now, we build the stub for the grailAttributes object. The stub contains an application context. On the application context, the getBean(beanName) function is called to retrieve the message source. The application context is retrieved from the grailsAttributes stub, by calling getApplicationContext() on the grailsAttributes stub. We have to build quite an object. That’s why we implemented a special method getGrailsAttributesStub() that does all the building:
      private getGrailsAttributesStub() {
        def applicationContextStubber = new StubFor(ApplicationContext)
        applicationContextStubber.demand.getBean() {beanName -> getBean(beanName) }
        def grailsAttributesStubber = new StubFor(Object)
        grailsAttributesStubber.demand.getApplicationContext() {
  3. getBean(beanName) is called to retrieve the actual messageSource bean. In our test, we only expect a messageSource bean to be retrieved, and nothing else. This yields the code:
      private getBean(beanName) {
        assertEquals "messageSource", beanName

messageSourceStubber is a class variable:

  private messageSourceStubber

We made this stubber a class variable, so we can define expected calls to the message source, within our tests:

    messageSourceStubber.demand.getMessage(1..1) {code, args, locale ->
      assertEquals "myclassarg.myproparg", code
      "property name from message source"

That’s it. Now you know how to replace the output stream and application context of a tag lib by your own mock or stub. Happy testing!


Tags: , , ,

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s

%d bloggers like this: