2012-01-30 85 views
3

我正在嘲笑(使用Mockito)DefaultMessageListenerContainerorg.springframework.jms.listener.DefaultMessageListenerContainer)。這裏是我的代碼:如何模擬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

+0

類似的問題:http://stackoverflow.com/questions/2457239/injecting-mockito-mocks-into-a-spring-bean – 2012-01-30 16:52:11

+0

你爲什麼*要*嘲笑它?你的代碼不應該依賴'DefaultMessageListenerContainer',它是一個Spring基礎組件。 – skaffman 2012-01-31 16:19:59

回答

1

您仍然需要對正在初始化的對象設置模擬。

我看到您正在測試IncomingFeedControllerImpl,並且很可能您的模擬對象是此類實例的成員。因爲你沒有明確地在你的IncomingFeedControllerImpl上設置你的模擬DefaultMessageListenerContainer,所以AbstractJmsListeningContainer(這可能是@Autowired)仍然處於閒置狀態,沒有被模擬。

您將需要使用setter方法或構造函數注入它。 (你也可以@Autowired吧)

+0

沒有。不是這種情況。試過了。這肯定與這個生命週期監視器沒有實例化有關。 – szymon 2012-01-30 17:11:44

+0

好吧,根據你的代碼,你試圖模擬你的'DefaultMessageListenerContainer',並通過接收'AbstractJmsListeningContainer',意味着你的'IncomingFeedController'上從來沒有設置模擬對象。 – 2012-01-30 17:15:35

+0

我只是中省略這一部分,但這裏是我的測試代碼: '@Test 公共無效testHandleConnectionState(){ 名單<使用DefaultMessageListenerContainer>聽衆=新的ArrayList <使用DefaultMessageListenerContainer>(); listeners.add(defaultMessageListenerContainer); incomingFeedController.setContainers(listeners); when(defaultMessageListenerContainer.isRunning()).thenReturn(false);' – szymon 2012-01-30 17:18:38

1

Mockito不能模擬最後的類或最終的方法。這些約束由JVM本身執行。嘲笑這樣的代碼將會要求重寫類字節碼並將其加載到另一個類加載器中。這導致非常複雜的代碼; PowerMock進入了這個方向,代碼難以維護。

也不要模擬你不擁有的類型,請參閱google上的4-5首個結果。爲什麼你需要在單元測試中模擬Spring類型。您應該創建一些間接方法來避免Spring依從性或編寫集成測試;後者似乎更合適,因爲它與JMS有關。

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

} 
+0

是的'@ PrepareForTest'指令是強制性的,否則PowerMock不會在暫時的PowerMock類加載器的領域_prepare_這個類 – Brice 2012-01-31 08:44:25

+0

編輯以反映這一點。 – jhericks 2012-01-31 16:03:41

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