Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Grails / Spock: How to mock single method within class where method is called from within the class itself?

Given the following, how do I mock processMessage() using Spock, so that I can check that processBulkMessage() calls processMessage() n times, where n is the number of messages within a BulkMessage?

class BulkMessage {
    List messages
}

class MyService {

    def processBulkMessage(BulkMessage msg) {
        msg.messages.each {subMsg->
            processMessage(subMsg)
        }
    }

    def processMessage(Message message) {

    }
}
like image 905
John Avatar asked Dec 12 '14 14:12

John


People also ask

Why do we need mocking framework?

A complete mocking framework like JustMock enables developers to focus solely on testing the system under test and forget about the distracting mocking details. Mock objects are created automatically in memory when the tests are run based on the simple configuration in the unit test.

What is Spock in Java?

Spock is a testing and specification framework for Java and Groovy applications. What makes it stand out from the crowd is its beautiful and highly expressive specification language. Thanks to its JUnit runner, Spock is compatible with most IDEs, build tools, and continuous integration servers.


2 Answers

You can use spies and partial mocks (requires Spock 0.7 or newer).

After creating a spy, you can listen in on the conversation between the caller and the real object underlying the spy:

def subscriber = Spy(SubscriberImpl, constructorArgs: ["Fred"])
subscriber.receive(_) >> "ok"

Sometimes, it is desirable to both execute some code and delegate to the real method:

subscriber.receive(_) >> { String message -> callRealMethod(); message.size() > 3 ? "ok" : "fail" }
like image 120
Gregor Petrin Avatar answered Sep 24 '22 13:09

Gregor Petrin


In my opinion this is not a well designed solution. Tests and design walk hand in hand - I recommend this talk to investigate it better. If there's a need to check if other method was invoked on an object being under test it seems it should be moved to other object with different responsibility.

Here's how I would do it. I know how visibility works in groovy so mind the comments.

@Grab('org.spockframework:spock-core:0.7-groovy-2.0')
@Grab('cglib:cglib-nodep:3.1')

import spock.lang.*

class MessageServiceSpec extends Specification {

    def 'test'() {
        given:
        def service = new MessageService()
        def sender = GroovyMock(MessageSender)

        and:
        service.sender = sender

        when:
        service.sendMessages(['1','2','3'])

        then:
        3 * sender.sendMessage(_)
    }
}
class MessageSender { //package access - low level   
   def sendMessage(String message) {
      //whatever
   }
}

class MessageService {

   MessageSender sender //package access - low level

   def sendMessages(Iterable<String> messages) {
      messages.each { m -> sender.sendMessage(m) }
   }
}
like image 32
Opal Avatar answered Sep 24 '22 13:09

Opal