5

I am sure this is quite a common question now, but I really can't get away with this issue I am having on mocking private method which internally calls another method and returns a collection. Class that I am testing has a public method which calls private method to get Collection object. I use PowerMock to create a spy of private method.

public void method1(String s)
{
     Collection<Object> list = invokePrivate()
}

private Collection<Object> invokePrivate()
{
     Wrapper wrapperObj = Factory.getInstance.getWrapper();
     Collection<Object> list = wrapperObj.callWrapperMethod(); // This always calls into real method, instead of mocked version.
     return list;
}

Test Class-:

So In order to test public method "method1" I create a spy using PowerMockito to spy over private method and return a demo list.

MainClass obj = new MainClass();
MainClass spy = PowerMockito.spy(obj);
PowerMockito.when(spy, method(MainClass.class, "inokePrivate"))
                            .thenReturn(list); // demo list which exists as a test class member.

Above calls into private method which in turns tries to call wrapperObj.callWrapperMethod() which resides in a different artifact and breaks there because some implementation it doesn't find there. So I try to mock wrapperObj.callWrapperMethod.

WrapperClass wr = new WrapperClass();
WrapperClass spy1 = PowerMockito.spy(wr);
when(spy1.callWrapperMethod()).thenReturn(list) // demo list which exists as a test class member.

Above mocking again calls into actual implementation of callWrapperMethod() and breaks in there. How can I prevent calling into actual implementation of wrapper method?

Few of the answers that helped me-:

Mockito:How to mock method called inside another method

Testing Private method using mockito

[UPDATE] -: as suggested as I did following-:

PowerMockito.doReturn(list).when(spy1).callWrapperMethod(); // This returns me demo list successfully.

But now when I call private method from PowerMockito control goes into invokePrivate method and again tries to call original callWrapperMethod instead of return list from spy version.

Community
  • 1
  • 1
Mr. Wonderful
  • 189
  • 4
  • 13
  • possible duplicate of [I am telling PowerMockito spy to return a value, so why is it calling the actual method?](http://stackoverflow.com/questions/27827892/i-am-telling-powermockito-spy-to-return-a-value-so-why-is-it-calling-the-actual) – durron597 Apr 21 '15 at 15:10
  • I appreciate the quick response ;-) – GhostCat Aug 08 '17 at 18:25

1 Answers1

3

I suggest to not do it this way. Your private method should not retrieve the singleton factory object using a static method.

Static stuff breaks "easy" mocking; forces you to use "power" mocking; and thereby, creates more problems than it solves.

Change your code to use dependency injection. Do something like this:

class YourClass {
  private final Factory factory;

  public YourClass() {
     this(Factory.getInstance(); }

  YourClass(Factory theFactory) {
     this.factory = theFactory;
  ...

This will allow you to use the second constructor in your unit test; to provide a (easily mocked) factory object for your class. Thereby you eliminate the whole need for PowerMock.

Long story short - when code is hard to test; change the code; and not the test. As a side effect, you are improving the quality of your code - because you loose the hard dependency on that singleton object.

And just to be complete: I also recommend to avoid "breaking" the Law of Demeter ( http://en.wikipedia.org/wiki/Law_of_Demeter ): if your class needs the wrapper; then it should hold a wrapper object; if it needs that factory; then it should hold a factory object. But you should not hold one object ... to retrieve another object from there, to run something on that second object. As you see - doing so leads exactly to the sort of problem that you are facing.

GhostCat
  • 137,827
  • 25
  • 176
  • 248
  • Using above I still need to call wrapperObj.callWrapperMethod() , where it which I've already mocked, since its in a different artifact and I don't want to call original method. On calling public method it always calls original callWrapperMethod instead of mock object. – Mr. Wonderful Apr 21 '15 at 15:37
  • What prevents your mocked factory to return a mocked wrapper? – GhostCat Apr 22 '15 at 10:32