如何模拟DefaultMessageListenerContainer

时间:2012-01-30 16:40:57

标签: java spring mocking mockito

我正在努力嘲笑(使用Mockito)DefaultMessageListenerContainerorg.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
    at org.springframework.jms.listener.AbstractJmsListeningContainer.isRunning(AbstractJmsListeningContainer.java:312)
    at com.source.etf.manager.integrationgateway.feedcontroller.IncomingFeedControllerTest.testHandleConnectionState(IncomingFeedControllerTest.java:37)
    at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
    at sun.reflect.NativeMethodAccessorImpl.invoke(Unknown Source)
    at sun.reflect.DelegatingMethodAccessorImpl.invoke(Unknown Source)
    at java.lang.reflect.Method.invoke(Unknown Source)
    at org.junit.internal.runners.TestMethod.invoke(TestMethod.java:59)
    at org.junit.internal.runners.MethodRoadie.runTestMethod(MethodRoadie.java:98)
    at org.junit.internal.runners.MethodRoadie$2.run(MethodRoadie.java:79)
    at org.junit.internal.runners.MethodRoadie.runBeforesThenTestThenAfters(MethodRoadie.java:87)
    at org.junit.internal.runners.MethodRoadie.runTest(MethodRoadie.java:77)
    at org.junit.internal.runners.MethodRoadie.run(MethodRoadie.java:42)
    at org.junit.internal.runners.JUnit4ClassRunner.invokeTestMethod(JUnit4ClassRunner.java:88)
    at org.junit.internal.runners.JUnit4ClassRunner.runMethods(JUnit4ClassRunner.java:51)
    at org.junit.internal.runners.JUnit4ClassRunner$1.run(JUnit4ClassRunner.java:44)
    at org.junit.internal.runners.ClassRoadie.runUnprotected(ClassRoadie.java:27)
    at org.junit.internal.runners.ClassRoadie.runProtected(ClassRoadie.java:37)
    at org.junit.internal.runners.JUnit4ClassRunner.run(JUnit4ClassRunner.java:42)
    at org.eclipse.jdt.internal.junit4.runner.JUnit4TestReference.run(JUnit4TestReference.java:50)
    at org.eclipse.jdt.internal.junit.runner.TestExecution.run(TestExecution.java:38)
    at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.runTests(RemoteTestRunner.java:467)
    at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.runTests(RemoteTestRunner.java:683)
    at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.run(RemoteTestRunner.java:390)
    at 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吗?

4 个答案:

答案 0 :(得分:1)

您仍然需要在正在初始化的对象上设置模拟。

我看到你正在测试IncomingFeedControllerImpl,你的模拟对象很可能是这个类的一个实例的成员。由于您没有明确地在DefaultMessageListenerContainer IncomingFeedControllerImpl上设置被模拟的AbstractJmsListeningContainer @Autowired(可能是@Autowired)仍然闲逛并且没有被嘲笑。

您需要使用setter方法或构造函数注入它。 (你也可以{{1}})

答案 1 :(得分:1)

Mockito无法模拟最终类或最终方法。这些约束由JVM本身强制执行。模拟这样的代码将要求实际重写类字节码并将其加载到另一个类加载器中。这导致代码非常复杂; PowerMock走向了这个方向,代码很难维护。

也不要模拟你不拥有的类型,请参阅google上的4-5个第一个结果。为什么需要在单元测试中模拟Spring类型。您应该创建一些间接来避免Spring遵从或编写集成测试;后者似乎更合适,因为它与JMS有关。

答案 2 :(得分:0)

您需要使用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 );
    }

}

答案 3 :(得分:0)

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);
    }
}