Grails : How I do mock other methods of a class under test which might be called internally during testing

16,395

Solution 1

For this kind of problem I actually avoid mocks and use the built-in groovyProxy ability to cast a map of closures as a proxy object. This gives you an instance with some methods overridden, but others passed through to the real class:

class SampleService {
    def methodA() {
        methodB()
    }

    def methodB() {
        return "real method"
    }
}

def mock = [methodB: {-> return "mock!" }] as SampleService

assert "mock!" == mock.methodA()
assert "real method" == new SampleService().methodA()

I like that only changes an instance, can be done in a single line, and doesn't mess with the metaclass of anything outside of that instance that needs to be cleaned up.

Solution 2

There are a lot of mocking alternatives with Groovy. You can see some documentation of Groovy Mocks, using Maps and Expandos instead of Mocks or using Closures instead of Mocks.

In your example, I'll use metaprogramming capabilities of Groovy.

void testMethodA() {
    service = new SampleService()
    service.metaClass.methodB = { -> return "what you want" }
    service.methodA()
    assert "your condition"
}
Share:
16,395
Ritesh M Nayak
Author by

Ritesh M Nayak

Thoughtworker living in Bangalore, India. Interested in Information Retrieval, Distributed Systems and ICT4D. Love music and code. My Website !!! I even got myself a cool stackoverflow careers resume. You can check that out here : My resume on Stackoverflow Careers

Updated on June 11, 2022

Comments

  • Ritesh M Nayak
    Ritesh M Nayak almost 2 years

    I am writing a test for methodA() in a service class similar to the one given below.

    Class SampleService {
      def methodA(){
         methodB()
      }
    
      def methodB(){
      }
    }
    

    When I test methodA(), I need to be able to mock the call to methodB() when testing methodA(). I am using version 2.0.x of grails. In the 1.3.x distributions, I would write a self mock like this

    def sampleServiceMock = mockFor(SampleService) 
    sampleServiceMock.demand.methodB { -> } 
    

    But this doesn't work in the 2.0.x versions. I was wondering what are the other ways of mocking methodB() when testing methodA()

  • Ritesh M Nayak
    Ritesh M Nayak about 12 years
    I did finally use this approach to mocking. But, semantically speaking, changing a class's signature to make a test pass seems extreme :)
  • Ritesh M Nayak
    Ritesh M Nayak about 12 years
    Yeah, this seems like a great way of mocking behaviors. I still miss the ability to self mock using grails' mockFor() :(
  • Domenic D.
    Domenic D. over 9 years
    Brilliant. This helped me as well as the test I had was creating Spock closures and was unable to assert the actual values returned from the controller (when mocking a service call).
  • GSAN
    GSAN about 7 years
    The links( Groovy Mocks, using Maps and Expandos instead of Mocks or using Closures instead of Mocks) are not working anymore, please fix or delete them. Thanks.
  • Pila
    Pila about 6 years
    What if the methods take in parameters. I am trying that an I keep getting; groovy.lang.MissingMethodException: No signature of method: ... is applicable for argument types: (java.lang.String, java.lang.String)