Re: [powermock] Impossible to mock protected method of instance

4,139 views
Skip to first unread message

Johan Haleby

unread,
May 10, 2013, 1:45:09 PM5/10/13
to powe...@googlegroups.com
What error message do you get?


On Fri, May 10, 2013 at 5:39 PM, Kaspars Plavins <kaspars...@gmail.com> wrote:
Hi, 

Here is the minimum amount of code to reproduce something that I suspect is a bug in PowerMockito.

Looks like the only way to mock protected method is to replace method, is there a more elegant solution to achieve this? I tried to do it with PowerMockito.when..  and doReturn, but that failed





package powermock;

public abstract class AbstractSave {

abstract void doSave();

protected boolean isSaved(String filename) {
throw new RuntimeException("Cought mocked method");
}

}



package powermock;

public class Save extends AbstractSave {

@Override
public void doSave() {
String s = "content";
boolean b = super.isSaved(s);
if (b) {
System.out.println("success");
}
}

}


package powermock;

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;

import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.powermock.api.mockito.PowerMockito;
import org.powermock.core.classloader.annotations.PrepareForTest;
import org.powermock.modules.junit4.PowerMockRunner;

@RunWith(PowerMockRunner.class)
@PrepareForTest({ Save.class, AbstractSave.class })
public class SaveTest {

Save save = new Save();

@Before
public void setup() throws Exception {

// does not have any effect
// PowerMockito.when((AbstractSave) save, "isSaved", "content")
// .thenReturn(true);

// fails with NPE
// PowerMockito.doReturn(true).when((AbstractSave) save, "isSaved",
// "content");

Method m = PowerMockito.method(AbstractSave.class, "isSaved",
String.class);
PowerMockito.replace(m).with(new InvocationHandler() {

public Object invoke(Object proxy, Method method, Object[] args)
throws Throwable {
return true;
}
});

}

@Test
public void testSave() {
Save save = new Save();
save.doSave();
}

}

--
You received this message because you are subscribed to the Google Groups "PowerMock" group.
To unsubscribe from this group and stop receiving emails from it, send an email to powermock+...@googlegroups.com.
To post to this group, send email to powe...@googlegroups.com.
Visit this group at http://groups.google.com/group/powermock?hl=en.
For more options, visit https://groups.google.com/groups/opt_out.
 
 

Kaspars Plavins

unread,
May 10, 2013, 4:23:59 PM5/10/13
to powe...@googlegroups.com
By using first commented out part I get RuntimeException from AbstractSave.. so no effect from PowerMockito.

If I use only second commented out part I get this

java.lang.NullPointerException
at org.powermock.api.mockito.internal.expectation.PowerMockitoStubberImpl.addAnswersForStubbing(PowerMockitoStubberImpl.java:67)
at org.powermock.api.mockito.internal.expectation.PowerMockitoStubberImpl.prepareForStubbing(PowerMockitoStubberImpl.java:124)
at org.powermock.api.mockito.internal.expectation.PowerMockitoStubberImpl.when(PowerMockitoStubberImpl.java:92)
at powermock.SaveTest.setup(SaveTest.java:24)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:39)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:25)
at java.lang.reflect.Method.invoke(Method.java:597)
at org.junit.internal.runners.MethodRoadie.runBefores(MethodRoadie.java:129)
at org.junit.internal.runners.MethodRoadie.runBeforesThenTestThenAfters(MethodRoadie.java:93)
at org.powermock.modules.junit4.internal.impl.PowerMockJUnit44RunnerDelegateImpl$PowerMockJUnit44MethodRunner.executeTest(PowerMockJUnit44RunnerDelegateImpl.java:296)
at org.powermock.modules.junit4.internal.impl.PowerMockJUnit49RunnerDelegateImpl$PowerMockJUnit49MethodRunner.executeTestInSuper(PowerMockJUnit49RunnerDelegateImpl.java:116)
at org.powermock.modules.junit4.internal.impl.PowerMockJUnit49RunnerDelegateImpl$PowerMockJUnit49MethodRunner.executeTest(PowerMockJUnit49RunnerDelegateImpl.java:77)
at org.powermock.modules.junit4.internal.impl.PowerMockJUnit44RunnerDelegateImpl$PowerMockJUnit44MethodRunner.runBeforesThenTestThenAfters(PowerMockJUnit44RunnerDelegateImpl.java:284)
at org.junit.internal.runners.MethodRoadie.runTest(MethodRoadie.java:84)
at org.junit.internal.runners.MethodRoadie.run(MethodRoadie.java:49)
at org.powermock.modules.junit4.internal.impl.PowerMockJUnit44RunnerDelegateImpl.invokeTestMethod(PowerMockJUnit44RunnerDelegateImpl.java:209)
at org.powermock.modules.junit4.internal.impl.PowerMockJUnit44RunnerDelegateImpl.runMethods(PowerMockJUnit44RunnerDelegateImpl.java:148)
at org.powermock.modules.junit4.internal.impl.PowerMockJUnit44RunnerDelegateImpl$1.run(PowerMockJUnit44RunnerDelegateImpl.java:122)
at org.junit.internal.runners.ClassRoadie.runUnprotected(ClassRoadie.java:34)
at org.junit.internal.runners.ClassRoadie.runProtected(ClassRoadie.java:44)
at org.powermock.modules.junit4.internal.impl.PowerMockJUnit44RunnerDelegateImpl.run(PowerMockJUnit44RunnerDelegateImpl.java:120)
at org.powermock.modules.junit4.common.internal.impl.JUnit4TestSuiteChunkerImpl.run(JUnit4TestSuiteChunkerImpl.java:101)
at org.powermock.modules.junit4.common.internal.impl.AbstractCommonPowerMockRunner.run(AbstractCommonPowerMockRunner.java:53)
// some more eclipse test runner errors

Johan Haleby

unread,
May 11, 2013, 2:18:33 AM5/11/13
to powe...@googlegroups.com
In order to use doReturn, when etc you first need to create a mock or spy. For example:

Save save = spy(new Save());

doReturn(true).when(save).isSaved("..");

This has nothing to do with PowerMock, just plain Mockito.

/Johan

Kaspars Plavins

unread,
May 11, 2013, 3:08:19 AM5/11/13
to powe...@googlegroups.com
Hi,

Thanks for the answer, the problem is that isSaved() is a protected method in parent class and is used from child class explicitly calling super.isSaved(), so mockito can not see it.

Of course in real code I do not have control over parent abstract class and it is in different package.

Kaspars Plavins

unread,
May 11, 2013, 3:42:57 AM5/11/13
to powe...@googlegroups.com
Just noticed that in my sample isSaved() has package visibility, actually it should be protected.

Johan Haleby

unread,
May 11, 2013, 12:08:23 PM5/11/13
to powe...@googlegroups.com
What you usually do is to place the test in the same package (but in the test folder) as the class under test. Thus you should be able to access the method. If this is not an option you have to something similar that what you're already doing.

Kaspars Plavins

unread,
May 13, 2013, 9:04:11 AM5/13/13
to powe...@googlegroups.com
Ok, a bit hardcore to replace method for all instances, but if that is the only way, then I can go with it.

What I was thinking about is to have something like replace integrated within the OngoingStubbing so then if I could do something like

// mock isSaved() method in instance of save parent.
PowerMockito.when(AbstractSave.class, save, "isSaved",
Mockito.eq("content")).thenReturn(true);

I think this can be done by replacing method with Invocation handler and saving reference to caller, so that all other calls can invoke original method, but if the caller is the instance mentioned in when() method, then it uses the stub. I am just having hard time to fogure out how to wrap it in OngoingStubbing.

Johan Haleby

unread,
May 13, 2013, 9:16:18 AM5/13/13
to powe...@googlegroups.com
Well you can do that already using something like this:

when(testInstance, "isSaved", eq("content").thenReturn(true);

/Johan
Reply all
Reply to author
Forward
0 new messages