【问题标题】:PowerMockito throws NullPointerException when trying to stub private overloaded method尝试存根私有重载方法时,PowerMockito 抛出 NullPointerException
【发布时间】:2015-08-25 15:49:49
【问题描述】:

我(仍在)尝试使用 PowerMockito 检查bar(Alpha, Baz) 是否调用了bar(Xray, Baz)(因为bar(Xray, Baz) 是private) - 没有实际调用后者,因为我的MCVE 类Foo 在下面。 (我上过同一个课程earlier,Foo 中的所有方法都是public - 以防你有似曾相识的感觉......)

public class Foo {
    private String bar(Xray xray, Baz baz) {
        return "Xray";
    }

    private String bar(Zulu zulu, Baz baz) {
        return "Zulu";
    }

    public String bar(Alpha alpha, Baz baz) {
        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;
        }
    }
}

当我尝试运行下面的测试时,我从 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),我会得到一个IllegalArgumentException: argument type mismatch at org.powermock.reflect.internal.WhiteboxImpl.performMethodInvocation(WhiteboxImpl.java:2014)。 (它与 NPE 在同一条线上冒泡。)

我正在使用 Mockito-core 1.9.5、PowerMock 1.5.4(module-junit4 和 api-mockito)和 JUnit 4.11。

我需要改变什么来阻止异常被抛出?我怎样才能使这个测试工作? (除了testing that 我的班级工作,而不是如何...;-))

【问题讨论】:

    标签: java powermock overloading private-members powermockito


    【解决方案1】:

    在设置期望值时,我们必须使用精确的参数匹配器。在你的情况下,它是 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));
       }
    }
    

    观察:当调用验证方法时,我们必须传递存根对象而不是实际对象,因为我们对存根对象设置了期望。由于我添加了断言语句来测试该方法,因此您不必在存根上验证它是否正常工作。

    添加: 我在公共和私有“bar”方法中都添加了 sysout 语句,当我再次测试时,我看到公共 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
    

    测试方法也通过了。以下是具有 sysouts 的 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(stub, times(5)).invoke("bar", Matchers.any(Xray.class), Matchers.any(Baz.class));,事实证明您正在检查您调用 String res = stub.bar(alpha, baz); 的频率 - 它似乎并没有查看 Foo 本身。这是有道理的,因为据我所知,any() 总是返回 void - 所以 PowerMock 无法在运行时检查类型 - 并且匹配 bar(Alpha, Baz)。
    • 将其更改为 Method m = Whitebox.getMethod(Foo.class, Xray.class, Baz.class); PowerMockito.doReturn("ok").when(stub, m).withArguments(any(Xray.class), any(Baz.class)); for times(5) 只会使其因 Wanted but not invoked: foo.bar(<any>, <any>); -> at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method) Actually, there were zero interactions with this mock. 而失败
    • @Christian,我的第一个答案没有模拟私人酒吧方法,而是模拟了公共酒吧方法,我使用 MemberMatcher.method 更正了测试方法,在我更新的答案中查看详细说明。
    猜你喜欢
    • 2018-11-29
    • 2018-05-03
    • 2011-04-13
    • 2014-07-15
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多