我正在努力模拟(使用Mockito)DefaultMessageListenerContainer
(org.springframework.jms.listener.DefaultMessageListenerContainer
)。这是我的代码:
@Mock
private DefaultMessageListenerContainer defaultMessageListenerContainer;
@Before
public void init() {
MockitoAnnotations.initMocks( this );
incomingFeedController = new IncomingFeedControllerImpl();
}
@Test
public void testHandleConnectionState() {
List< DefaultMessageListenerContainer > listeners =
new ArrayList< DefaultMessageListenerContainer >();
listeners.add( defaultMessageListenerContainer );
incomingFeedController.setContainers( listeners );
when( defaultMessageListenerContainer.isRunning() ).thenReturn( false );
}
然后我想做一些适当的测试,比如:
when( defaultMessageListenerContainer.isRunning() ).thenReturn( false );
但在junit运行后,这条线最终显示:
java.lang.NullPointerException位于org.springframework.jms.elistener.AbstractJmsListeningContainer.isRunning(AbstractJmsListingContainer.java:312)网址:com.source.etf.manager.integrationgateway.feedcontroller.IncomingFeedControllerTest.testHandleConnectionState(IncomingFeedController Test.java:37)在sun.reflect.NativeMethodAccessorImpl.invoke0(本机方法)位于sun.reflect.NativeMethodAccessorImpl.invoke(未知源)在sun.reflect.DelegatingMethodAccessorImpl.invoke(未知源)位于java.lang.reflect.Method.ioke(未知源)网址:org.junit.internal.runners.TestMethod.ioke(TestMethod.java:59)网址:org.junit.internal.runners.MethodRoadie.runTestMethod(MethodRoadie.java:98)网址:org.junit.internal.runners.MethodRoadie$2.run(MethodRoadie.java:79)网址:org.junit.internal.runners.MethodRoadie.runBeforestThenAfters(MethodRoadie.java:87)网址:org.junit.internal.runners.MethodRoadie.runTest(MethodRoadie.java:77)网址:org.junit.internal.runners.MethodRoadie.run(MethodRoadie.java:42)网址:org.junit.internal.runners.JUnit4ClassRunner.invokeTestMethod(JUnit4CClassRunner.java:88)网址:org.junit.internal.runners.JUnit4ClassRunner.runMethods(JUnit4CClassRunner.java:51)网址:org.junit.internal.runners.JUnit4ClassRunner$1.run(JUnit4CClassRunner.java:44)网址:org.junit.internal.runners.ClassRoadie.runUnprotected(ClassRoadie.java:27)网址:org.junit.internal.runners.ClassRoadie.runProtected(ClassRoadie.java:37)网址:org.junit.internal.runners.JUnit4ClassRunner.run(JUnit4CClassRunner.java:42)网址:org.eclipse.jdt.internal.junit4.runner.JUnit4TestReference.run(JUnit4TtestReference.java:50)网址:org.eclipse.jdt.internal.junit.runner.TestExecution.run(TestExecution.java:38)网址:org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.runTests(RemoteTestRunner.java:467)网址:org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.runTests(RemoteTestRunner.java:683)网址:org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.run(RemoteTestRunner.java:390)网址:org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.main(RemoteTestRunner.java:197)
我还检查了AbstractJmsListeningContainer
,这是发生NPE的代码:
public final boolean isRunning() {
synchronized (this.lifecycleMonitor) {
return (this.running && runningAllowed());
}
}
我发现lifecycleMonitor
对象没有被实例化。此对象在AbstractJmsListeningContainer
中的顶部声明:
protected final Object lifecycleMonitor = new Object();
知道如何正确地嘲笑DefaultMessageListenerContainer
吗?
您仍然需要在正在初始化的对象上设置mock。
我看到您正在测试IncomingFeedControllerImpl
,并且您的模拟对象很可能是此类实例的成员。因为你没有在你的IncomingFeedControllerImpl
上明确地设置你的模拟DefaultMessageListenerContainer
,所以AbstractJmsListeningContainer
(可能是@Autowired
)仍然在附近,没有被模拟。
您需要使用setter方法或构造函数来注入它。(您也可以@Autowired
it)
Mockito不能模拟final类或final方法。这些约束是由JVM本身强制执行的。模拟这样的代码需要实际重写类字节码并将其加载到另一个类加载器中。这导致了非常复杂的代码;PowerMock正朝着这个方向发展,代码很难维护。
也不要嘲笑你不拥有的类型,在谷歌上看到4-5个第一个结果。为什么您需要在单元测试中模拟Spring类型。您应该创建一些间接方法来避免Spring的遵守,或者编写集成测试;后者似乎更合适,因为它与JMS有关。
您要么需要使用PowerMock,要么按照@Brice的建议进行集成测试。以下是如何在PowerMock中做到这一点:
@RunWith(PowerMockRunner.class)
@PrepareForTest(DefaultMessageListenerContainer.class)
public class MyTestClass {
// cannot use the @Mock annotation
private DefaultMessageListenerContainer defaultMessageListenerContainer;
@Before
public void init() {
// This should allow the mocking of final methods as well.
defaultMessageListenerContainer =
PowerMockito.mock(DefaultMessageListenerContainer.class);
incomingFeedController = new IncomingFeedControllerImpl();
}
@Test
public void testHandleConnectionState() {
List< DefaultMessageListenerContainer > listeners =
new ArrayList< DefaultMessageListenerContainer >();
listeners.add( defaultMessageListenerContainer );
incomingFeedController.setContainers( listeners );
when( defaultMessageListenerContainer.isRunning() ).thenReturn( false );
}
}
private DefaultMessageListenerContainer createFailingContainer() {
DefaultMessageListenerContainer container = new DefaultMessageListenerContainer() {
@Override
public void start() throws JmsException {
throw new MyJmsException("TEST");
}
@Override
public void stop() throws JmsException {
throw new MyJmsException("TEST");
}
};
return container;
}
class MyJmsException extends JmsException {
private static final long serialVersionUID = 1L;
public MyJmsException(String msg) {
super(msg);
}
}