使用Mockito存根测试方法调用时出现NullPointerException

时间:2016-11-17 14:21:27

标签: java mockito

我在单元测试中有这两行:

DefaultMessageListenerContainer defaultMessageListenerContainer = Mockito.mock(DefaultMessageListenerContainer.class);
when(defaultMessageListenerContainer.isRunning()).thenReturn(true);

堆栈追踪:

java.lang.NullPointerException
    at org.springframework.jms.listener.AbstractJmsListeningContainer.isRunning(AbstractJmsListeningContainer.java:347)
    at xxxxxxxxxxxxxx.MessageProcessorControllerTest.testPing(MessageProcessorControllerTest.java:109)
    at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
    at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
    at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
    at java.lang.reflect.Method.invoke(Method.java:497)
    at org.testng.internal.MethodInvocationHelper.invokeMethod(MethodInvocationHelper.java:85)
    at org.testng.internal.Invoker.invokeMethod(Invoker.java:639)
    at org.testng.internal.Invoker.invokeTestMethod(Invoker.java:816)
    at org.testng.internal.Invoker.invokeTestMethods(Invoker.java:1124)
    at org.testng.internal.TestMethodWorker.invokeTestMethods(TestMethodWorker.java:125)
    at org.testng.internal.TestMethodWorker.run(TestMethodWorker.java:108)
    at org.testng.TestRunner.privateRun(TestRunner.java:774)
    at org.testng.TestRunner.run(TestRunner.java:624)
    at org.testng.SuiteRunner.runTest(SuiteRunner.java:359)
    at org.testng.SuiteRunner.runSequentially(SuiteRunner.java:354)
    at org.testng.SuiteRunner.privateRun(SuiteRunner.java:312)
    at org.testng.SuiteRunner.run(SuiteRunner.java:261)
    at org.testng.SuiteRunnerWorker.runSuite(SuiteRunnerWorker.java:52)
    at org.testng.SuiteRunnerWorker.run(SuiteRunnerWorker.java:86)
    at org.testng.TestNG.runSuitesSequentially(TestNG.java:1191)
    at org.testng.TestNG.runSuitesLocally(TestNG.java:1116)
    at org.testng.TestNG.run(TestNG.java:1024)
    at org.testng.IDEARemoteTestNG.run(IDEARemoteTestNG.java:72)
    at org.testng.RemoteTestNGStarter.main(RemoteTestNGStarter.java:124)

无论我做什么,NPE都会在when行上抛出,就好像在设置存根时调用真正的方法一样。我正在使用mockito v1.10.19。我做错了什么?

更新 以下是整个独立测试类:

import org.mockito.Mockito;
import org.springframework.jms.listener.DefaultMessageListenerContainer;
import org.testng.annotations.Test;

import static org.mockito.Mockito.when;   

public class JmsTest {
    @Test
    public void test() throws Exception {
        DefaultMessageListenerContainer defaultMessageListenerContainer = Mockito.mock(
                DefaultMessageListenerContainer.class);
        when(defaultMessageListenerContainer.isRunning()).thenReturn(true);
    }
}

引发的异常:

java.lang.NullPointerException
    at org.springframework.jms.listener.AbstractJmsListeningContainer.isRunning(AbstractJmsListeningContainer.java:347)
    at xxxxxxxxxxx.util.JmsTest.test(JmsTest.java:17)
    at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
    at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
    at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
    at java.lang.reflect.Method.invoke(Method.java:497)
    at org.testng.internal.MethodInvocationHelper.invokeMethod(MethodInvocationHelper.java:85)
    at org.testng.internal.Invoker.invokeMethod(Invoker.java:639)
    at org.testng.internal.Invoker.invokeTestMethod(Invoker.java:816)
    at org.testng.internal.Invoker.invokeTestMethods(Invoker.java:1124)
    at org.testng.internal.TestMethodWorker.invokeTestMethods(TestMethodWorker.java:125)
    at org.testng.internal.TestMethodWorker.run(TestMethodWorker.java:108)
    at org.testng.TestRunner.privateRun(TestRunner.java:774)
    at org.testng.TestRunner.run(TestRunner.java:624)
    at org.testng.SuiteRunner.runTest(SuiteRunner.java:359)
    at org.testng.SuiteRunner.runSequentially(SuiteRunner.java:354)
    at org.testng.SuiteRunner.privateRun(SuiteRunner.java:312)
    at org.testng.SuiteRunner.run(SuiteRunner.java:261)
    at org.testng.SuiteRunnerWorker.runSuite(SuiteRunnerWorker.java:52)
    at org.testng.SuiteRunnerWorker.run(SuiteRunnerWorker.java:86)
    at org.testng.TestNG.runSuitesSequentially(TestNG.java:1191)
    at org.testng.TestNG.runSuitesLocally(TestNG.java:1116)
    at org.testng.TestNG.run(TestNG.java:1024)
    at org.testng.IDEARemoteTestNG.run(IDEARemoteTestNG.java:72)
    at org.testng.RemoteTestNGStarter.main(RemoteTestNGStarter.java:124)
    at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
    at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
    at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
    at java.lang.reflect.Method.invoke(Method.java:497)
    at com.intellij.rt.execution.application.AppMain.main(AppMain.java:147)

2 个答案:

答案 0 :(得分:0)

您的defaultMessageListenerContainer不是正在使用的人。失败的是AbstractJmsListeningContainer类型。

您需要确保将mocked变量正确传递给您要测试的类。

答案 1 :(得分:0)

这是一个很好的!我已经预料到测试会通过,因为它似乎只是设定了预期,然后没有断言。我想我会把它留给Mockito专家,为什么这个看起来很简单的练习不起作用。我当然有兴趣知道背后的原因。同时,如果你可以选择使用另一个库,我会提供这个建议:试试JMockit。以下应该大致相当于你试图用Mockito实现的......但是测试通过了。祝你好运!

import mockit.Expectations;
import mockit.Mocked;
import org.junit.Test;
import org.springframework.jms.listener.DefaultMessageListenerContainer;

import static org.assertj.core.api.Assertions.assertThat;

public class JmsTest2 {

    @Mocked
    DefaultMessageListenerContainer defaultMessageListenerContainer;

    @Test
    public void test() throws Exception {
        new Expectations() {{
            defaultMessageListenerContainer.isRunning(); result = true;
        }};
        assertThat(defaultMessageListenerContainer.isRunning()).isTrue();
    }
}

编辑1:

仔细研究一下,看起来围绕标记为final的对象的isRunning()方法存在一些同步。 Mockito,AFAIK,如果你有一些标记为final的对象你想要改变,那么它往往没那么有用。你可以试着用PowerMock来对抗它,或者使用不同的库 - 我已经建议了我喜欢的那个 - 我认为这对你来说是一个干净而简单的方法。

protected final Object lifecycleMonitor = new Object();
...
@Override
public final boolean isRunning() {
    synchronized (this.lifecycleMonitor) {
        return (this.running && runningAllowed());
    }
}