Sunday, 3 June 2007

Grails 0.5.5 - testing controllers with Mocks and Expando

In this blog entry I would like to show you how to unit test a typical 'update' action in Grails using Groovy Mocks and the ExpandoMetaClass.

Let's assume we have an action like this:
 
 def updateword = {
     def word = Word.get( params.word_id )
     if(word) {
         word.pl = params.word_translation
         if(word.save()) {
             render("OK")
         }
         else {
             render("Error updating word")
         }
     }
     else {
         render("Word does not exist")
     }
 }
In brief, it updates a Word instance by getting a Word from the database, modifying its 'pl' property and saving it back to the database.

Now, how would you unit test the execution path where the "Error updating word" message gets rendered? To do that, you need a scenario where a Word instance gets returned from the database but fails to save back after the update operation. Here is how you can do it using Mocks and Expandos:
 void testUpdateExistingWordSaveFailure() {
     def mock = new MockFor(Word)
     def fakeWord = new Expando(save: {false})
 
     mock.demand.get { fakeWord }
     mock.use {
         def wc = new WordController()
         wc.params.word_translation = "tak"
         wc.updateword()
         assertEquals "Error updating word", 
                      wc.response.delegate.contentAsString
     }
 }

The flow of the test is as follows:
  1. Create a mock for the Word class
  2. Craete a fake word instance (Expando) which replaces the 'save' method
  3. Demand the Word class to return the faked word when the get method is called (exactly once)
  4. Use the mock and call the updateword closure
  5. Assert if the correct message was rendered to the browser
See the Unit & Integration Testing Controllers page to understand the last step - getting a text from the response.