2011-10-26 93 views
5

我想單元測試我在Spring中創建的自定義事件,並且遇到了一個有趣的問題。如果我創建了一個StaticApplicationContext並手動註冊並連接bean,我可以觸發事件,並通過發佈者(實現ApplicationEventPublisherAware)到偵聽器(實現ApplicationListener<?>)看到程序流。單元測試Spring ApplicationEvents - 事件正在發佈,但偵聽器未觸發?

然而,當我嘗試創建一個JUnit測試創建使用SpringJunit4ClassRunner@ContextConfiguration一切正常除了ApplicationEvents未顯示在收聽了上下文(我已經證實,他們所得到出版)。

是否有一些其他方式來創建上下文,以便ApplicationEvents可以正常工作?我沒有在網上找到關於單元測試Spring事件框架的知識。

+2

爲什麼有這個問題沒有答案 –

回答

1

事件不會觸發,因爲您的測試類未從Spring應用程序上下文(它是事件發佈者)進行註冊和解析。

我已經實現了一個解決方法,其中事件在另一個類中進行處理,該類以Spring的身份註冊爲bean並作爲測試的一部分解析。這並不美麗,但是在浪費了一天中最好的時間尋找更好的解決方案之後,我現在對此感到滿意。

我的用例在RabbitMQ用戶收到消息時觸發了一個事件。它是由以下內容組成:

包裝類

注意的init()函數,從測試調用傳遞迴調函數測試

內從容器解析後
public class TestEventListenerWrapper { 

CountDownLatch countDownLatch; 
TestEventWrapperCallbackFunction testEventWrapperCallbackFunction; 

public TestEventListenerWrapper(){ 

} 

public void Init(CountDownLatch countDownLatch, TestEventWrapperCallbackFunction testEventWrapperCallbackFunction){ 

    this.countDownLatch = countDownLatch; 
    this.testEventWrapperCallbackFunction = testEventWrapperCallbackFunction; 
} 

@EventListener 
public void onApplicationEvent(MyEventType1 event) { 

    testEventWrapperCallbackFunction.CallbackOnEventFired(event); 
    countDownLatch.countDown(); 
} 

@EventListener 
public void onApplicationEvent(MyEventType2 event) { 

    testEventWrapperCallbackFunction.CallbackOnEventFired(event); 
    countDownLatch.countDown(); 
} 

@EventListener 
public void onApplicationEvent(OnQueueMessageReceived event) { 

    testEventWrapperCallbackFunction.CallbackOnEventFired(event); 
    countDownLatch.countDown(); 
} 
} 

回調接口

public interface TestEventWrapperCallbackFunction { 

void CallbackOnEventFired(ApplicationEvent event); 
} 

測試配置類定義在單元測試中引用的bean。在此之前是非常有用的,它需要從ApplicationContext的解決,initialsed(見下一步)

@Configuration 
public class TestContextConfiguration { 
    @Lazy 
    @Bean(name="testEventListenerWrapper") 
    public TestEventListenerWrapper testEventListenerWrapper(){ 
     return new TestEventListenerWrapper(); 
    } 
} 

最後,解析從ApplicationContext中的bean並調用初始化單元測試本身()函數傳遞斷言條件(假定您已將bean註冊爲單例 - Spring applicationContext的默認值)。回調函數在這裏定義並且也傳遞給Init()。

@ContextConfiguration(classes= {TestContextConfiguration.class, 
           //..., - other config classes 
           //..., - other config classes 
           }) 
public class QueueListenerUnitTests 
     extends AbstractTestNGSpringContextTests { 

    private MessageProcessorManager mockedMessageProcessorManager; 
    private ChannelAwareMessageListener queueListener; 

    private OnQueueMessageReceived currentEvent; 

    @BeforeTest 
    public void Startup() throws Exception { 

     this.springTestContextPrepareTestInstance(); 
     queueListener = new QueueListenerImpl(mockedMessageProcessorManager); 
     ((QueueListenerImpl) queueListener).setApplicationEventPublisher(this.applicationContext); 
     currentEvent = null; 
    } 

    @Test 
    public void HandleMessageReceived_QueueMessageReceivedEventFires_WhenValidMessageIsReceived() throws Exception { 

     //Arrange 
     //Other arrange logic 
     Channel mockedRabbitmqChannel = CreateMockRabbitmqChannel(); 
     CountDownLatch countDownLatch = new CountDownLatch(1); 

     TestEventWrapperCallbackFunction testEventWrapperCallbackFunction = (ev) -> CallbackOnEventFired(ev); 
     TestEventListenerWrapper testEventListenerWrapper = (TestEventListenerWrapper)applicationContext.getBean("testEventWrapperOnQueueMessageReceived"); 
     testEventListenerWrapper.Init(countDownLatch, testEventWrapperCallbackFunction); 

     //Act 
     queueListener.onMessage(message, mockedRabbitmqChannel); 
     long awaitTimeoutInMs = 1000; 
     countDownLatch.await(awaitTimeoutInMs, TimeUnit.MILLISECONDS); 

     //Assert - assertion goes here 
    } 

    //The callback function that passes the event back here so it can be made available to the tests for assertion 
    private void CallbackOnEventFired(ApplicationEvent event){ 
     currentEvent = (OnQueueMessageReceived)event; 
    } 
} 
  • EDIT 1:樣本代碼已更新,CountDownLatch
  • 編輯2:斷言因此上述用不同的方法更新沒有失敗試驗**
0

您可以手動創建上下文。

例如:我需要檢查,如果我的ApplicationListener<ContextClosedEvent>關閉卡桑德拉連接:

@Test 
public void testSpringShutdownHookForCassandra(){ 
    ConfigurableApplicationContext ctx = new AnnotationConfigApplicationContext(CassandraConfig.class); 

    CassandraConnectionManager connectionManager = ctx.getBean(CassandraConnectionManager.class); 
    Session session = connectionManager.openSession(testKeySpaceName); 

    Assert.assertFalse(session.isClosed()); 
    ctx.close(); 

    Assert.assertTrue(session.isClosed()); 
} 
相關問題