Grails mock objects: mockFor vs. Gmock

When writing Grails unit tests it’s often very useful to use mock objects, especially when testing service calls from controllers. Grails includes built-in support for mock objects via mockFor. The mockFor mock objects support the full mock lifecycle: record, play, and verify.

In this post, I’ll compare writing a unit test with Grails mockFor mock objects and an alternative Groovy mocking framework: Gmock

Suppose I’m testing this controller code. In the unit test I’m going to use mock services to verify the correct service methods are called, and with the right arguments.

To test this controller with Grails’ mockFor to create the mock services, I’d write a test case something like this:

The ‘demand’ calls set up the expectation that the controller calls the service methods I expect. Next, I create the mock service instances with the createMock() calls. Then the two calls to .verify() at the end of the test case actually check that the expected methods were called.

And sure enough, when I run this test it passes:

| Running 1 unit test... 1 of 1
--Output from mockForTestCase--
| Completed 1 unit test, 0 failed in 1745ms
| Tests PASSED - view reports in targettest-reports

But what happens if I break the controller code by putting in the wrong method arguments to one of the services, maybe put the wrong NotificationType in the notificationService call?

notificationService.notifyAccountHolder(accountName, NotificationType.ACCOUNT_CREATION)

When I run the test, it still passes!

| Running 1 unit test... 1 of 1
--Output from mockForTestCase--
| Completed 1 unit test, 0 failed in 1696ms
| Tests PASSED - view reports in targettest-reports

Wait, my controller code is wrong but my unit test still passes? My test could definitely use some improvement. Specifically, I need to start verifying the method arguments are correct. So I’ll update my test to check the method arguments:

And now when I run the test, it catches the broken controller code:

| Failure: mockForTestCaseWithArgumentMatchers(BankControllerTests)
| Assertion failed:

assert notificationTypeCalled == NotificationType.ACCOUNT_REMOVAL
| | |

at BankControllerTests.mockForTestCaseWithArgumentMatchers(BankControllerTests.groovy:80)

Great, my test now catches the errror! But looking back at the unit test – I had to write quite a bit of code just to verify the method arguments were correct. So much extra code that my unit test case is now more than twice as long as the controller class I’m testing. While a longer unit test isn’t a bad thing in itself, I’m lazy – I don’t want to write all this extra code if I don’t have to. Is there a shorter, more concise way to write the same unit test?

To find out, I’ll write a similar unit test using Gmock. To get started with Gmock, first I’ll include it as a dependency in my BuildConfig.groovy:

And then I’ll add the @WithGMock annotation on my unit test class:

Now I’m ready to starting using Gmock. I’ll write the unit test using mock services created with Gmock instead of mockFor. For space considerations I won’t delve too deeply into the Gmock syntax, but there is a great tutorial on the Gmock website for more specifics on the powerful Gmock syntax.

And when I run the test when the controller code is correct, the test indeed passes:

| Running 1 unit test... 1 of 1
--Output from gmockTestCase--
| Completed 1 unit test, 0 failed in 1986ms
| Tests PASSED - view reports in targettest-reports

But what if I break the controller like I did before and put in the wrong notification type?

--Output from gmockTestCase--
| Failure: gmockTestCase(BankControllerTests)
| junit.framework.AssertionFailedError: Unexpected method call 'notifyAccountHolder('AccountToRemove', ACCOUNT_CREATION)' on 'Mock for NotificationService'
'removeAccount('AccountToRemove')' on 'Mock for BankService': expected 1, actual 1
'notifyAccountHolder('AccountToRemove', ACCOUNT_REMOVAL)' on 'Mock for NotificationService': expected 1, actual 0

Sure enough, the Gmock test case catches the failure. Why? Because the Gmock test includes argument matchers in its syntax. And the mock expectation calls look almost exactly like the real code, with just a .returns() at the end, whereas all the code I wrote in the mockFor .demand calls looks very little like my code under test. With Gmock, I don’t have to switch coding contexts between production code and unit test – they are nearly identical. As an added bonus, I even get code complete for the mock service calls in my IDE.

Now Gmock isn’t a perfect fit for all mock situations. For example, with mockFor mocks you can use one of the method arguments as the return value. And Gmock doesn’t support testing multi-threaded methods (yet). But in many unit tests with mock objects, Gmock allows for more concise and readable unit tests.

One thought on “Grails mock objects: mockFor vs. Gmock

  1. I think you are missing out on Spock and it’s built-in mocking features. That’s what I’ve been using for exactly this scenario and I have never looked back.

    Tests and mocking behaviour are much easier to read. Have a look at the spock grails plugin…

  2. exdevfr says:

    Completely agree. While upgrading a small app to 2.x I tried the built-in mock support once more.
    – It’s verbose
    – Maybe I missed something but I had to revert to metaClass magic for stuff like partial mock and mocking constructor calls.

  3. Craig Atkinson says:

    Cool, thanks for the suggestions about checking out Spock! I’ve used Spock a little bit for data-driven testing but haven’t used its mocking features yet – I’ll be sure to give its mocks a try.

Leave a Reply

Your email address will not be published. Required fields are marked *