How to mock a return value of a private method in spock test
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...
photosynthesis
Updated on June 04, 2022Comments
-
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 about 8 yearsWhich mocking framework are you using?
-
Todd W Crone about 8 yearsHave 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 about 8 yearsSorry, looks like I was wrong here. I thought I had intercepted private calls in Groovy this way before.
-
-
photosynthesis about 8 yearsThanks 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 about 8 yearsYou 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 about 8 yearscan I put all these steps in 'given'? I tried but didn't seem to work
-
Jérémie B about 8 yearsthis answer is on "why you CAN access private method", not why you can't
-
photosynthesis about 8 yearsI 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 about 8 yearsit work only in groovy code, not java. only groovy know about metaclasses. if you call java code, then the metaclasses are ignored
-
photosynthesis about 8 yearsok, any suggestion for this? i assume this should be quite normal testing Java code using Groovy, right?
-
Jérémie B about 8 yearsmy only suggestion is to improve your code to make it testable ;-) Abstract components behavior behind interfaces, use integration test with in-memory db, etc.