2

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;
}
photosynthesis
  • 2,632
  • 7
  • 29
  • 45
  • Which mocking framework are you using? – user2004685 Feb 19 '16 at 16:28
  • 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 Feb 23 '16 at 01:43
  • Sorry, looks like I was wrong here. I thought I had intercepted private calls in Groovy this way before. – Todd W Crone Feb 23 '16 at 13:43

2 Answers2

2

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...

Jérémie B
  • 10,611
  • 1
  • 26
  • 43
  • can I put all these steps in 'given'? I tried but didn't seem to work – photosynthesis Feb 19 '16 at 16:42
  • 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? – photosynthesis Feb 19 '16 at 18:04
  • it work only in groovy code, not java. only groovy know about metaclasses. if you call java code, then the metaclasses are ignored – Jérémie B Feb 19 '16 at 18:07
  • ok, any suggestion for this? i assume this should be quite normal testing Java code using Groovy, right? – photosynthesis Feb 19 '16 at 18:24
  • 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. – Jérémie B Feb 19 '16 at 18:27
0

You can't mock private methods using Mockito. But if there is an explicit need then you can try looking at PowerMock.

It is not expected to write the tests for private methods as they get covered when you write the tests for public methods.

If any of your mocks are being called in the private method then you can verify the calls by doing something like this:

Mockito.verify(myMock, Mockito.times(1)).myMethod(myParams,...,...);
user2004685
  • 9,548
  • 5
  • 37
  • 54
  • 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. – photosynthesis Feb 19 '16 at 16:36
  • 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: http://stackoverflow.com/questions/26464980/why-can-test-private-methods-fields-in-spock-without-problems – user2004685 Feb 19 '16 at 16:39
  • this answer is on "why you CAN access private method", not why you can't – Jérémie B Feb 19 '16 at 17:27