2011-10-26 11 views
5

Sto provando a testare unitamente gli eventi personalizzati che ho creato in primavera e sto incontrando un problema interessante. Se creo un StaticApplicationContext e registro e leghi manualmente i fagioli, posso attivare gli eventi e vedere il flusso del programma attraverso l'editore (implementa ApplicationEventPublisherAware) attraverso il listener (implementa ApplicationListener<?>).Unit Testing Spring ApplicationEvents - Gli eventi vengono pubblicati ma gli ascoltatori non sparano?

Eppure, quando cerco di creare un test JUnit per creare il contesto utilizzando il SpringJunit4ClassRunner e @ContextConfiguration tutto funziona bene tranne che le ApplicationEvents non vengono visualizzati in chi ascolta (mi hanno confermato che stanno ottenendo pubblicati).

C'è un altro modo per creare il contesto in modo che ApplicationEvents funzioni correttamente? Non ho trovato molto sul web riguardo alle unità che testano il framework degli eventi di primavera.

+2

perciò non ci sono risposta per questa domanda –

risposta

1

Gli eventi non vengono attivati ​​perché le classi di test non sono registrate e risolte dal contesto dell'applicazione di primavera, che è l'editore dell'evento.

Ho implementato una soluzione alternativa per questo in cui l'evento viene gestito in un'altra classe registrata con Spring come bean e risolta come parte del test. Non è bello, ma dopo aver sprecato la parte migliore di un giorno cercando di trovare una soluzione migliore, sono contento di questo per ora.

Il mio caso di utilizzo ha generato un evento quando viene ricevuto un messaggio all'interno di un utente RabbitMQ. Si compone delle seguenti operazioni:

L'involucro classe

Nota la Init) funzione (che viene chiamata dal test di passare nella funzione di callback dopo aver risolto dal contenitore all'interno del test

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

L'interfaccia di callback

public interface TestEventWrapperCallbackFunction { 

void CallbackOnEventFired(ApplicationEvent event); 
} 

Una classe di configurazione di prova per definire il bean a cui viene fatto riferimento nel test dell'unità. Prima di questo è utile, dovrà essere risolto dal applicationContext e initialsed (vedi passo successivo)

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

Infine, la prova stessa unità che risolve il fagiolo dalla applicationContext e chiama il Init() Funzione per passare i criteri di asserzione (questo presuppone che tu abbia registrato il bean come un singleton - il valore predefinito per Spring applicationContext). La funzione di callback è qui definita e anche passata a 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: Il codice di esempio è stato aggiornato con CountDownLatch
  • EDIT 2: asserzioni non sicuro test in modo che il sopra è stato aggiornato con un approccio diverso **
0

È possibile creare manualmente un contesto.

Per esempio: avevo bisogno di verificare se il mio ApplicationListener<ContextClosedEvent> chiuso connessioni Cassandra:

@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()); 
} 
Problemi correlati