Sunday, April 25, 2010

Unit testing Grails Controller - Part - 3

Alright folks this is the third and final installment of Unit Testing Grails Controller series.

In the last post I explained how we could write a test case where controller returns a model that could be used by view to render itself.

In this post we will see how we could write tests for situations in which controller has to render a view (may be to show validation errors while saving a instance of domain class)

Tired of repeating, but will say it again anyways, taking an example of a controller that performs CRUD on Person domain class lets call it PersonController

Requirement:

If an error occurs while saving a person, controller should re-render the create view showing the error so that user can rectify the error and try again.

Controller Code:
The controller code that will do the job for us looks like

How will we test it - How do they do it!:

Again, in the grails environment, save method on a person instance will actually fire an insert statement, but do we really want to do that while writing a unit test for PersonController?  The right answer is NO.  10 Points if you get that right!

So what are we doing in the above code sample.
  1. Creating a person instance using the request parameters
  2. Try to save the person created in the previous step
  3. If save fails render the create view and return from there (Guard clause is an import pattern which deserves a post for itself)
  4. If save succeeds redirect user to show view.
We should have a unit-test, for each of the above steps.
We already know how to write test case for step 4 from the first post

Lets focus on the first three steps.  There are two ways in which we can test the above code.  A longer and a shorter way!  And yes you guessed it, I will show the longer way first :)

Method 1: 
  1. use mockDomain(Person) to add the save dynamic method to person instance
  2. From the test code, we could populate the params map with invalid values
  3. Those params would be used to construct the Person instance
  4. While saving the person instance constraints will be fired, which will fail because of invalid values
  5. This will cause the save method to return false and hence if condition will be satisfied causing the render to execute.
  6. ControllerUnitTestCase class provides us a convenient property called modelAndView using which we can assert that controller actually renders to the create view
The test class code to demonstrate the above method is as follows

Although we should have written another test case to assert that the model was correctly populate by the controller but for the sake of this blog I have shown the assert in single test case.

Now that you have the Grails power, don't go anywhere!  I promise, will not bore you for long.  Here's the Method 2 of testing the above code.

Method 2:
  1. Stub out the save method (on the person instance) to return false manually, using the metaClass of Person
  2. use the modelAndView property to assert that controller actually renders the create view
  3. Remember to remove the metaClass using which we stubbed the save method using the GroovySystem class.  If you don't do this then the metaClass setting will interfere with other tests that use the save method.  Because of this, some test cases could misteriously start failing when, run along with other tests and pass when run individually.  Such a situation is extremely difficult to debug and fix.  This is the single most common mistake people do while using metaClass.  Take my advice, Dont do it!
Short and simple isn't it.  lets look at the test code.

See its pretty easy to randomly add/stub methods using the metaClass.  Its a really powerful tool but as Spider Man's uncle Ben once said
With great power comes great responsibility!
use it responsibly!

Here, I conclude the three part series to demonstrate art of Unit Testing the grails controller

I hope you liked reading it as much as I liked writing it.  Feedback, Comments and Complaints are welcome as always!
Have some Fun!