Using Mock Annotation and mockFor in Grails Unit Tests

More and more often, when reading documentation, I find myself thinking of the lyrics from The Kinks song Lola:

… well I’m not dumb, but I can’t understand…

Too often, I find the documentation to be sparse and the examples included too simple. A gap exists from reading the documentation, looking at the included examples and transposing that unto real-world problems that are not quite that simple.

My current project at work is using Grails 2.0.x and I was very glad to see that it included a whole collection of unit testing mixins. In particular, there were three things I wanted to start using right away when testing a service:

  • TestFor annotation
  • Mock annotation
  • Defining mocks and stubs programtically

Now although that the documentation is indeed sparse, it turns out that it actually is very concise. The documentation states that:

Most testing can be achieved via the TestFor annotation in combination with the Mock annotation for mocking collaborators

@Mock([Book, Author, BookService])

followed by

The Mock@ annotation creates mock version of any collaborators. There is an in-memory implementation of GORM that will simulate most interactions with the GORM API. For those interactions that are not automatically mocked you can use the built in support for defining mocks and stubs programmatically. For example:

def control = mockFor(SearchService)
control.demand.searchWeb { String q -> ['mock results'] }
control.demand.static.logResults { List results ->  }
controller.searchService = control.createMock()

I first really understood what it said after playing around with it for a while. It is here that some real-life examples along with a few words of explanation would have been very helpful. So here goes with an example from the real world – hopefully this can get you going full speed with unit test mixins a little faster than I could.

I have a Service that receives an event that a piece of musik has been played on one of our radio stations. First I need to retrieve the channel, determine which program played it and finally call a rest-service that returns all that we know about this particular piece of musik, and then persist this particular track played.

So the service I need to test, looks like this when stripped down to its essence:

class TrackPlayedEventService {
  def musicService

  void handleTrackPlayedEvent(TrackPlayedEvent event) {
    def channel = Channel.findByName(event.channelName)
    def program = Program.findByChannelAndStartTimeLessThanOrEqualsAndEndTimeGreaterThanOrEquals(
        channel, event.startTime, event.endTime)
    def musikInfo = musicService.getTrackInfo(event.trackId)
    def trackPlayed = new TrackPlayed(trackId: event.trackId, program: program, 
        salesArtists: musikInfo.salesArtists, trackTitle: musikInfo.title)

Now in the above example, I need to have mock versions of Channel and Program as well as the resulting object of type TrackPlayed with basic GORM functionality, and to stub the musicService.getTrackInfo(trackId) method. Therefore, the Mock annotation is perfect for Channel, Program, and TrackPlayed, but for the MusikService I need to use the mockFor() method, as I need some real data returned packaged in a MusikInfo object.

@Mock([Channel, Program, TrackPlayed])
class TrackPlayedEventServiceTests {
  def eventObject
  void setup() {
    eventObject = new TrackPlayedEvent(channelName: 'P3', trackId: '1234567-1-1', startTime:
        Date.parse('yyyy-MM-dd HH:mm:ss', '2012-03-23 11:23:08'), 
        endTime: Date.parse('yyyy-MM-dd HH:mm:ss', '2012-03-23 11:27:46'))
    // Use the Channel and Program mocks created by @Mock annotation to create a channel and program
    def myChannel = new Channel(name: 'P3') false)
    new Program(channel: myChannel, startTime: Date.parse('yyyy-MM-dd HH:mm:ss', '2012-03-23 11:00:00'),
        endTime: Date.parse('yyyy-MM-dd HH:mm:ss', '2012-03-23 13:00:00')).save(validate: false)

  void testHandleTrackPlayedEvent() {
    // create mockFor service and stub the method that is to be intercepted
    def mockService = mockFor(MusicService)
    // NOTE: if the method you're mocking has zero arguments remember to start your closure with {->
    // else it will complain because of the implicit it argument to a closure
    mockService.demand.getTrackInfo {trackId ->
      return getMusikInfoObject()
    service.musicService = mockService.createMock()

    // Using the TrackPlayed mock - find (hopefully) the TrackPlayed persisted by the service method under test
    def trackPlayed = TrackPlayed.findByTrackId('1234567-1-1')
    assert trackPlayed 
    assert trackPlayed.title == 'My Song Title'

  MusikInfo getMusikInfoObject()
    return new MusikInfo(trackId: '1234567-1-1', title: 'My Song Title', salesArtists: ['Artist one', 'Artist two'])

Hopefully this has been a help to you, which means that I am not alone in taking my time to fully understand the available documentation. But in case it is only me that is slow on the uptake, I’ll take comfort in the saying that ignorance is bliss.

5 thoughts on “Using Mock Annotation and mockFor in Grails Unit Tests

  1. Nice article…
    Testing documentation confuses lil bit..
    This helped to get some basic understanding

  2. Interesting article. I was unable to use this because in my case I am using an object that is created inside the function I am testing. @Mock wasn’t working for me either because the addTo* function I needed was not auto-mocked. It turns out the solution for this case is to make the auto-mocking aware of the collection by defining:

    def myCollection = []

    … in the domain class in addition to my previous version:

    static hasMany = [myCollection: MyCollectedClass]

    I added the extra def and with @Mock(MyClassWithMyCollection) my unit test of a function that creates the object and then calls addToMyCollection(myObj) is working just fine.

  3. Oops! Doh!!!

    The code above didn’t work (you can delete the post if you like). I made the wrong assumption when I got a different error.

    The final version (after moving the object creation outside of the test function) is:

    class A {
    def bs = []
    static hasMany = [bs: B]

    class B {

    class AService {
    void doA(a) {
    a.addToBs(new B())

    class AServiceTests {
    void testDoA() {
    def a = new A()
    a.metaClass.addToBs = { it ->}
    a.addToBs(new B())

    I would love to find a more elegant way to do this.

  4. This is a nice little example, good post. I definitely agree the doc on this is concise and confusing at first, but does take on more meaning after some usage. I landed here looking for more perspective on mockFor, but this reminds me of some things I noticed about @Mock early on with Grails. It really seems only useful if you’re using GORM. I have a project where the majority of persistence is not relational and doesn’t touch GORM, and subsequently have little to no use for @Mock.

Leave a Reply

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