Monday, 21 May 2012

Using Groovy's mockFor closure

I'm writing my Grails application and have come to a point where I realized that I need to start writing some tests before things get real ugly. I think as a software developer tests are invaluable to any project and I'm a firm believer in tests. With that I would like to share with you my experience and the gotchas of the mockFor closure.

Groovy 2.x provides us with mixins in our tests so that we don't have to extend other test classes (something that I also implemented at work) with the main goal being "favor composition over inheritance".  I like this approach. However, that means that magical methods can come out from anywhere, and if there's a problem, you have to dig deeper to find the answer. Case in point, consider this test code that I have written:




      def void setUp() { 
           def control = mockFor(SpringSecurityService)
           control.demand.getPrincipal { return new User() }
           controller.springSecurityService = control.createMock()
      }  



The code seems simple enough -- we create a mock for the SpringSecurityService, and then when a call to getPrincipal() is invoked, it would return a new user.

However, when I run it, this is what I get:



 | Failure: testIndex(com.tweetcommendation.BusinessControllerTests) 
 | groovy.lang.MissingMethodException: No signature of method: com.tweetcommendation.Business.findByUser() is applicable for argument types: (com.tweetcommendation.BusinessControllerTests$_setUp_closure1) values: [com.tweetcommendation.BusinessControllerTests$_setUp_closure1@3ee545fa]
      at org.grails.datastore.gorm.finders.DynamicFinder.createFinderInvocation(DynamicFinder.java:264)
      at org.grails.datastore.gorm.finders.DynamicFinder.invoke(DynamicFinder.java:150)
      at org.grails.datastore.gorm.finders.DynamicFinder.invoke(DynamicFinder.java:352)
      at org.grails.datastore.gorm.GormStaticApi.methodMissing(GormStaticApi.groovy:108)
      at com.tweetcommendation.BusinessController.index(BusinessController.groovy:14)
      at com.tweetcommendation.BusinessControllerTests.testIndex(BusinessControllerTests.groovy:25)  


Okay, so what happened? I read through the docs, and then realized one thing: if the mocking method doesn't have any input parameters, then you still need to add the -> param.

So, the code should look like this instead:



      def void setUp() { 
           def control = mockFor(SpringSecurityService)
           control.demand.getPrincipal { -> return new User() }
           controller.springSecurityService = control.createMock()
      }  














No comments:

Post a Comment