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))
for
times(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;
    }
}