Wednesday, August 26, 2009

Testing GSP Custom Tags

In Grails: A Quick-Start Guide we used several custom tags. Unfortunately we didn't get a chance to write any tests for these tags. So let's do that now.

In GQuick we have a loginToggle tag which will display a "login" link if no user is logged in and a "logout" link if a user is logged in.


  def loginToggle = {

    out << "<div>"

    if (session.user){

      out << "<span>"

      out << "Welcome ${session.user}."

      out << "</span><span>"

      out << "<a href='${createLink(controller:'tekUser',  

                         action:'logout')}'>"

      out << "Logout </a></span>"

    }

    else{

      out << "<span>"

      out << "<a href='${createLink(controller:'tekUser',

                         action:'login')}'>"

      out << "Login </a></span>"

    }

    out << "</div><br/>"

  }




As you can see from the tag code, we are storing a reference to our user object in the session. If we find a user we display a welcome message and a Logout link. If there is no user we just display the Login link. Let see how we can write a unit test for this tag.

When we create a taglib with the grails create-tag-lib command, a corresponding test is created for us which extends the TagLibUnitTestCase class. This class makes testing taglibs a piece of cake. Take a look at the following test:


    void testLoginToggleWithUser() {

        mockSession.user = "user"

        def output = tagLib.loginToggle().toString()

        assert output.contains('Logout')

        assert output.contains('action=logout')

    }




The first thing you'll notice is that we are setting the value of user on the mockSession object. This is one of the mock objects that are included in the TagLibUnitTestCase. Also included are mockRequest, mockResponse, mockParams, and mockFlash. Any calls to session in our tag code will be directed to this mockSession. So, when we look for the existence of a user to determine which text to display, we will find one.

In the next line we call the tag as a method of the implicit tagLib object. This object is loaded for us by the TagLibUnitTestCase. We don't need to declare or create it. It's just there and ready for us to use. Every tag in the tagLib can be called as a method. All of these tag methods return an instance of java.io.StringWriter. We call toString() on this to get a String representation of what the tag would have sent to the page.

Next we just makes some assertions on that output. We know that, if a user is in the session, we should have the word 'Logout' in the tag's output, so we can check for that with the contains() method. We'll talk about the second assertion in a minute. But first, if we were to run this test it would fail with an error because out tag calls the createLink tag that comes with Grails and none of the built-in Grails tags are available to unit tests.

Groovy makes this easy to handle. Let's just add the following line to our test class' setUp() method.

tagLib.metaClass.createLink = {map -> "action=${map.action}"}

Make sure to add this after the call to super.setUp() so that the tagLib object will be there. Now when a call to createLink is made within our tagLib it will call the method on the tagLib.metaClass which will return a string with the name of the action that the createLink would have linked to. This allows us to verify that we are passing the correct action, in this case 'logout'.

To be thorough, we'll also write a test to verify the behavior of our tag when there is no user logged in.

    void testLoginToggleNoUser() {

        def output = tagLib.loginToggle().toString()

        assert output.contains('Login')

        assert output.contains('action=login')

    }







Now that we see how easy it is to write unit tests for Grails tagLibs we have no excuse for leaving those auto-generated test classes empty.

No comments:

Post a Comment