使用PowerMock模拟私有方法,但仍会调用底层方法

我试图嘲笑模拟一个正在进行JNDI调用的私有方法。 当从unit testing调用该方法时,它会抛出exception^。 我想嘲笑这种方法用于测试目的。 我使用了来自另一个问题答案的示例代码 ,并且在测试通过时,似乎仍然调用了基础方法。 我在doTheGamble()方法中插入了一个System.err.println() ,然后将其打印到我的控制台。

有趣的是,如果我注释掉第一个assertThat ,那么测试就会通过。 ?:(

那么,我如何模拟私有方法,以便它不被调用?

 import static org.hamcrest.core.Is.is; import static org.junit.Assert.assertThat; import static org.mockito.Matchers.anyInt; import static org.mockito.Matchers.anyString; import static org.powermock.api.mockito.PowerMockito.when; import static org.powermock.api.support.membermodification.MemberMatcher.method; import java.util.Random; 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(CodeWithPrivateMethod.class) public class PowerMock_Test { static boolean gambleCalled = false; @Test(expected = RuntimeException.class) public void when_gambling_is_true_then_always_explode() throws Exception { CodeWithPrivateMethod spy = PowerMockito.spy(new CodeWithPrivateMethod()); when(spy, method(CodeWithPrivateMethod.class, "doTheGamble", String.class, int.class)) .withArguments(anyString(), anyInt()) .thenReturn(true); /* 1 */ assertThat( PowerMock_Test.gambleCalled, is(false) ); spy.meaningfulPublicApi(); /* 2 */ assertThat( PowerMock_Test.gambleCalled, is(false) ); } } class CodeWithPrivateMethod { public void meaningfulPublicApi() { if (doTheGamble("Whatever", 1 <>> GAMBLE CALLED <<<\n" ); PowerMock_Test.gambleCalled = true; return gamble; } } 

^可以理解,因为我的工作区不支持JNDI,只有生产环境才支持JNDI

%我使用的是所有库的最新版本,JUnit 4.10,Mockito 1.8.5,Hamcrest 1.1,Javassist 3.15.0和PowerMock 1.4.10。

从PowerMock私有方法示例 :

 @RunWith(PowerMockRunner.class) // We prepare PartialMockClass for test because it's final or we need to mock private or static methods @PrepareForTest(PartialMockClass.class) public class YourTestCase { @Test public void privatePartialMockingWithPowerMock() { PartialMockClass classUnderTest = PowerMockito.spy(new PartialMockClass()); // use PowerMockito to set up your expectation PowerMockito.doReturn(value).when(classUnderTest, "methodToMock", "parameter1"); // execute your test classUnderTest.execute(); // Use PowerMockito.verify() to verify result PowerMockito.verifyPrivate(classUnderTest, times(2)).invoke("methodToMock", "parameter1"); } 

因此,要将此应用于您的代码,我认为它可能会变为:

 @RunWith(PowerMockRunner.class) @PrepareForTest(CodeWithPrivateMethod.class) public class PowerMock_Test { @Test(expected = RuntimeException.class) public void when_gambling_is_true_then_always_explode() throws Exception { CodeWithPrivateMethod spy = PowerMockito.spy(new CodeWithPrivateMethod()); PowerMockito.doReturn(true).when(spy, "doTheGamble", anyString(), anyInt()); /* 1 */ PowerMockito.verifyPrivate(spy, times(0)).invoke("doTheGamble", anyString(), anyInt()); spy.meaningfulPublicApi(); /* 2 */ PowerMockito.verifyPrivate(spy, times(2)).invoke("doTheGamble", anyString(), anyInt()); } } 

我刚刚在编辑器中编写了这个代码。 实际上没有运行任何测试,并且在编写此代码时没有损坏任何错误。

ARTB,

只是粘贴完整的代码,它在我的Eclipse IDE中运行良好。 我只是改变了我在上一篇文章中所说的期望。 祝你好运。

 import static org.hamcrest.core.Is.is; import static org.junit.Assert.assertThat; import static org.mockito.Matchers.anyInt; import static org.mockito.Matchers.anyString; import static org.powermock.api.support.membermodification.MemberMatcher.method; import java.util.Random; 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(CodeWithPrivateMethod.class) public class PowerMock_Test { static boolean gambleCalled = false; @Test(expected = RuntimeException.class) public void when_gambling_is_true_then_always_explode() throws Exception { CodeWithPrivateMethod spy = PowerMockito.spy(new CodeWithPrivateMethod()); // PowerMockito.doReturn(true).when(spy, "doTheGamble", anyString(), anyInt()); PowerMockito.doReturn(true).when(spy, method(CodeWithPrivateMethod.class, "doTheGamble", String.class, int.class)) .withArguments(anyString(), anyInt()); assertThat( PowerMock_Test.gambleCalled, is(false) ); spy.meaningfulPublicApi(); assertThat( PowerMock_Test.gambleCalled, is(false) ); } } class CodeWithPrivateMethod { public void meaningfulPublicApi() { if (doTheGamble("Whatever", 1 << 3)) { throw new RuntimeException("boom"); } } private boolean doTheGamble(String whatever, int binary) { Random random = new Random(System.nanoTime()); boolean gamble = random.nextBoolean(); System.err.println( "\n>>> GAMBLE CALLED <<<\n" ); PowerMock_Test.gambleCalled = true; return gamble; } } 

ARTB,

你确定你的代码不起作用(或)我在这里遗漏了什么吗? 我刚刚用Mike建议的方式替换了你的方法期望,它工作正常:

 PowerMockito.doReturn(true).when(spy, method(CodeWithPrivateMethod.class, "doTheGamble", String.class, int.class)) .withArguments(anyString(), anyInt()); 

我从未使用过Powermockito,但之前使用过Mockito。

当你使用spy构造一个模拟对象时,它是一个真正的半支持对象。 我的猜测是摆脱spy 。 处理模拟对象。