Java PowerMockito在尝试存根私有重载方法时抛出NullPointerException
我(仍然)试图检查Java PowerMockito在尝试存根私有重载方法时抛出NullPointerException,java,powermock,overloading,private-members,powermockito,Java,Powermock,Overloading,Private Members,Powermockito,我(仍然)试图检查bar(Alpha,Baz)是否使用PowerMockito调用了bar(Xray,Baz)(因为bar(Xray,Baz)是私有的),而没有实际调用后者,因为我的MCVE类Foo。(我上过同样的课,Foo中的所有方法都是public——以防你有似曾相识的经历……) 当我尝试运行下面的测试时,我从PowerMock获得了一个NPE: @RunWith(PowerMockRunner.class) // @PrepareOnlyThisForTest(Foo.class) //
bar(Alpha,Baz)
是否使用PowerMockito调用了bar(Xray,Baz)
(因为bar(Xray,Baz)
是私有的
),而没有实际调用后者,因为我的MCVE类Foo
。(我上过同样的课,Foo
中的所有方法都是public
——以防你有似曾相识的经历……)
当我尝试运行下面的测试时,我从PowerMock获得了一个NPE:
@RunWith(PowerMockRunner.class)
// @PrepareOnlyThisForTest(Foo.class) // we aren't looking at the byte code, I think
public class FooTest {
@Test
public void testBar_callsBarWithXray() throws Exception {
Baz baz = new Baz(); //POJOs
Alpha alpha = new Alpha();
alpha.set(new Xray());
Foo foo = new Foo();
Foo stub = spy(foo); // using Mockito, as it's neither final nor "not spyable"
// NPE at org.powermock.api.mockito.internal.expectation.PowerMockitoStubberImpl.addAnswersForStubbing(PowerMockitoStubberImpl.java:67)
PowerMockito.doReturn("ok").when(stub, "bar", Xray.class, Baz.class);
stub.bar(alpha, baz);
// Testing if bar(Xray, Baz) was called by bar(Alpha, Baz)
PowerMockito.verifyPrivate(foo).invoke("bar", Xray.class, Baz.class);
// Mockito's equivalent for a public method: verify(stub, times(1)).bar(any(Xray.class), any(Baz.class));
}
}
如果我将存根设为PowerMockito.spy(foo)
,则会在org.powermock.reflect.internal.WhiteboxImpl.performMethodInvocation(WhiteboxImpl.java:2014)处得到一个IllegalArgumentException:参数类型不匹配。(它与NPE在同一条线上冒泡。)
我正在使用Mockito core 1.9.5、PowerMock 1.5.4(模块junit4和api Mockito)和junit4.11
我需要更改什么来阻止抛出异常?我怎样才能使这个测试有效?(除了我的类,而不是如何…;-)设置期望值时,我们必须使用精确的参数匹配器。在你的情况下,是这样的
Matchers.any(Xray.class)、Matchers.any(Baz.class)
我修改了您的代码,如下所示,并在测试方法的输出对象上添加了断言语句
@RunWith(PowerMockRunner.class)
//@PrepareOnlyThisForTest(Foo.class) // we aren't looking at the byte code, I think
public class FooTest {
@Test
public void testBar_callsBarWithXray() throws Exception {
Baz baz = new Baz(); //POJOs
Alpha alpha = new Alpha();
alpha.set(new Xray());
Foo foo = new Foo();
Foo stub = PowerMockito.spy(foo); // using Mockito, as it's neither final nor "not spyable"
// NPE at org.powermock.api.mockito.internal.expectation.PowerMockitoStubberImpl.addAnswersForStubbing(PowerMockitoStubberImpl.java:67)
PowerMockito.doReturn("ok").when(stub, "bar", Matchers.any(Xray.class), Matchers.any(Baz.class));
String res = stub.bar(alpha, baz);
Assert.assertEquals("ok", res);
//Testing if bar(Xray, Baz) was called by bar(Alpha, Baz)
PowerMockito.verifyPrivate(stub).invoke("bar", Matchers.any(Xray.class), Matchers.any(Baz.class));
// Mockito's equivalent for a public method: verify(stub, times(1)).bar(any(Xray.class), any(Baz.class));
}
}
观察:当调用verify方法时,我们必须传递存根对象而不是实际对象,因为我们对存根对象设置了期望值。当我添加assert语句来测试该方法时,您不必在存根上验证它是否正常工作
添加:
我在public和private“bar”方法中都添加了sysout语句,当我再次测试时,我看到public bar方法的sysout语句没有打印出来。
这意味着上面的代码只模拟了公共方法,而不是私有方法
为了模拟私有的“bar”方法,我尝试了另一种使用MemberMatcher.method进行模拟的方法,这种方法非常有效
import org.powermock.api.support.membermodification.MemberMatcher;
@RunWith(PowerMockRunner.class)
@PrepareForTest(Foo.class) // we need this
public class FooTest {
@Test
public void testBar_callsBarWithXray() throws Exception {
Baz baz = new Baz(); //POJOs
Alpha alpha = new Alpha();
alpha.set(new Xray());
Foo stub = PowerMockito.spy(new Foo());
PowerMockito.doReturn("ok")
.when(stub,
MemberMatcher.method(Foo.class,
"bar",
Xray.class, Baz.class))
.withArguments(Matchers.any(Xray.class), Matchers.any(Baz.class));
String res = stub.bar(alpha, baz);
Assert.assertEquals("ok", res);
//Testing if bar(Xray, Baz) was called by bar(Alpha, Baz)
PowerMockito.verifyPrivate(stub).invoke("bar", Matchers.any(Xray.class), Matchers.any(Baz.class));
// Mockito's equivalent for a public method: verify(stub, times(1)).bar(any(Xray.class), any(Baz.class));
}
output : public bar
试验方法也通过了。下面是具有sysout的foo方法
private String bar(Xray xray, Baz baz) {
System.out.println("private bar");
return "Xray";
}
public String bar(Alpha alpha, Baz baz) {
System.out.println("public bar");
if(alpha.get() instanceof Xray) {
return bar((Xray)alpha.get(), baz);
} else if(alpha.get() instanceof Zulu) {
return bar((Zulu)alpha.get(), baz);
} else {
return null;
}
}
嗯,如果我把它改成PowerMockito.verifyPrivate(存根,乘以(5)).invoke(“bar”,Matchers.any(Xray.class),Matchers.any(Baz.class))代码>结果表明,您正在检查调用String res=stub.bar(alpha,baz)的频率代码>-它似乎不看Foo
本身。这很有意义,因为据我所知,any()
总是返回void
,因此PowerMock无法在运行时检查类型,并且匹配bar(Alpha,Baz)
。将其更改为方法m=Whitebox.getMethod(Foo.class,Xray.class,Baz.class)代码>PowerMockito.doReturn(“ok”).when(stub,m).带有参数(any(Xray.class)、any(Baz.class))
fortimes(5)
只会使用命令使其失败,但不会调用:foo.bar(,);->在sun.reflect.NativeMethodAccessorImpl.invoke0(本机方法)上,实际上,与此模拟没有任何交互。
@Christian,我的第一个答案没有模拟私有条方法,而是模拟公共条方法,我使用MemberMatcher.Method更正了测试方法,请查看更新答案中的详细说明。
private String bar(Xray xray, Baz baz) {
System.out.println("private bar");
return "Xray";
}
public String bar(Alpha alpha, Baz baz) {
System.out.println("public bar");
if(alpha.get() instanceof Xray) {
return bar((Xray)alpha.get(), baz);
} else if(alpha.get() instanceof Zulu) {
return bar((Zulu)alpha.get(), baz);
} else {
return null;
}
}