Grails 2.0 mockFor() Gotcha

Update: When I was researching another Grails issue, I found out that what I encountered below looks to be fixed in Grails 2.0.1. I have yet to verify the fix, but if you also encounter this issue I urge you to try Grails 2.0.1 to see if it resolves the problem.

I am working on a Grails 2.0.x project and am quite enjoying some of the new features that are part of the current Grails release. The new test mixins that are part of Grails 2.0.x are awesome and really simplify the creation of your unit test classes. But I ran across a “gotcha” when using the test mixins.

I was writing a unit test class for a Grails controller, and was using the default template that gets created via:

grails create-controller com.asoftwareguy.example.Example

One of the helper methods exposed by the GrailsUnitTestMixin is the mockFor() method, which is a simplified way of creating a new Groovy mock. I though this to be much cleaner than needing to instantiate a new MockFor() every time I needed a mock proxy for an interface. However, I ran into an issue where the proxy instances created by the mockFor() method of the mixin are not cleaned up between test case runs. Let me give an example of what was failing.

ExampleController.groovy


package com.asoftwareguy.example

class ExampleController {

  def exampleService

  def index() {
    def something = exampleService.doSomething()
    render (view: 'index', model: [something: something])
  }
}

ExampleService.groovy

package com.asoftwareguy.example

interface ExampleService {
  List doSomething();
}

ExampleControllerTests.groovy

package com.asoftwareguy.example

@TestFor(ExampleController)
class ExampleControllerTests {

  void testDoSomething_first_time() {
    def mockService = mockFor(ExampleService) //use the mixin
    mockService.demand.doSomething() { ->
      return true
    }
    controller.exampleService = mockService.createMock()
    controller.index()
    // assertions
  }

  void testListStations_second_time() {
    def mockService = mockFor(ExampleService) //use the mixin
    mockService.demand.doSomething() { ->
      return true
    }
    controller.exampleService = mockService.createMock()
    controller.index()
    // assertions
  }
}

With the above code in place, the first test case would always pass, but the second test case would fail with the following error:

| Failure:  testListStations_second_time(com.asoftwareguy.example.ExampleControllerTests)
|  junit.framework.AssertionFailedError: No more calls to 'doSomething' expected at this point. End of demands.
at grails.test.MockClosureProxy.doBeforeCall(MockClosureProxy.java:66)
at grails.test.AbstractClosureProxy.call(AbstractClosureProxy.java:74)
at com.asoftwareguy.example.ExampleController.index(ExampleController.groovy:xx)
at com.asoftwareguy.example.ExampleControllerTests.testListStations_second_time(ExampleControllerTests.groovy:xx)
| Completed 2 unit tests, 1 failed

After some troubleshooting and debugging the issue, I have concluded that there is a bug in the version of Grails I running (2.0.0) and hopefully it is fixed in the next release. As a workaround, I have reverted to instantiating new MockFor() instances within each test case.

This entry was posted in Grails, Groovy and tagged , , , , , , , , . Bookmark the permalink.

4 Responses to Grails 2.0 mockFor() Gotcha

  1. I ran into the exactly same problem with Grails 2.0.0 yesterday. Glad to say that upgrade to Grails 2.0.3 fixed it.

  2. Marco Scata says:

    I get this same situation using Grails 2.2.0 now. Looks like the GORM dynamic methods are completely wiped out after the first test.

  3. penny says:

    2.2.4 too

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 )

Connecting to %s

This site uses Akismet to reduce spam. Learn how your comment data is processed.