How to mock a return value of a private method in spock test

14,460

Spock mock classes by using cglib proxies. Such proxy can't mock final classes or private methods (as private method are implicitly final). If your code under test is written in Groovy (like a script, or a grails application), then you can use Spock GroovyMock or patch the metaclass :

setup:
  HandlerClass.metaClass.test = { true }

given: "a handler"
  def handler = new HandlerClass()

when: "i call test" 
  def r = handler.test()

then:
  r == true

However, you should probably focus more on the testability of your code. Having to mock classes is generally not a good sign about the maintainability and testability of the code...

Share:
14,460
photosynthesis
Author by

photosynthesis

Updated on June 04, 2022

Comments

  • photosynthesis
    photosynthesis almost 2 years

    I want to test a public method in which it calls another private method, I used the following reflection way to get the private method and tried to mock the return value of it, but it didn't work as the test stops at where the private call is. Any suggestions?

    Method testMethod = handler.getClass().getDeclaredMethod("test", String.class)
    testMethod.setAccessible(true)
    testMethod.invoke(handler, "test string") >> true
    

    The testMethod looks like the following:

    private boolean test(String str) {
        return true;
    }
    
    • user2004685
      user2004685 about 8 years
      Which mocking framework are you using?
    • Todd W Crone
      Todd W Crone about 8 years
      Have you tried simply replacing the method via the metaClass. It would be better to replace a dependency used by the private method rather than try to replace the method. But if the class is Groovy, you should be able to do the following: instance.metaClass.test = { String s -> return true } to intercept the call in that instance.
    • Todd W Crone
      Todd W Crone about 8 years
      Sorry, looks like I was wrong here. I thought I had intercepted private calls in Groovy this way before.
  • photosynthesis
    photosynthesis about 8 years
    Thanks but the situation is I may have to mock that private method as it calls the database service at run time which is not preferred in the unit test. Besides, I don't use Mockito here but spock and groovy.
  • user2004685
    user2004685 about 8 years
    You should be mocking the DB Calls and not the private method. Here is a good answer on why you can't mock private methods using Spock + Groovy: stackoverflow.com/questions/26464980/…
  • photosynthesis
    photosynthesis about 8 years
    can I put all these steps in 'given'? I tried but didn't seem to work
  • Jérémie B
    Jérémie B about 8 years
    this answer is on "why you CAN access private method", not why you can't
  • photosynthesis
    photosynthesis about 8 years
    I used HandlerClass.metaClass.test = { String str -> true } to override the method but still no luck, does the override feature not work with private method?
  • Jérémie B
    Jérémie B about 8 years
    it work only in groovy code, not java. only groovy know about metaclasses. if you call java code, then the metaclasses are ignored
  • photosynthesis
    photosynthesis about 8 years
    ok, any suggestion for this? i assume this should be quite normal testing Java code using Groovy, right?
  • Jérémie B
    Jérémie B about 8 years
    my only suggestion is to improve your code to make it testable ;-) Abstract components behavior behind interfaces, use integration test with in-memory db, etc.