2011-10-26 7 views
5

Estoy intentando probar los eventos personalizados que he creado en Spring y me encuentro con un problema interesante. Si creo un StaticApplicationContext y registro y conecto manualmente los beans, puedo desencadenar eventos y ver el flujo del programa a través del editor (implementa ApplicationEventPublisherAware) a través del oyente (implementa ApplicationListener<?>).Unit Testing Spring ApplicationEvents - ¿Los eventos se están publicando pero los oyentes no están disparando?

Sin embargo, cuando intento crear una prueba JUnit para crear el contexto usando SpringJunit4ClassRunner y @ContextConfiguration, todo funciona bien, excepto que los ApplicationEvents no se muestran en el oyente (he confirmado que se están publicando).

¿Hay alguna otra forma de crear el contexto para que ApplicationEvents funcione correctamente? No he encontrado mucho en la web sobre la prueba unitaria del marco de eventos de primavera.

+2

no hay respuesta para esta pregunta –

Respuesta

1

Los eventos no se activarán porque las clases de prueba no están registradas y resueltas desde el contexto de la aplicación de primavera, que es el editor del evento.

Implementé una solución para esto donde el evento se maneja en otra clase que se registra con Spring como un bean y se resuelve como parte de la prueba. No es lindo, pero después de perder la mejor parte de un día tratando de encontrar una mejor solución, estoy contento con esto por ahora.

Mi caso de uso estaba disparando un evento cuando se recibe un mensaje dentro de un consumidor de RabbitMQ. Se compone de los siguientes:

El contenedor de clase

Nota la Init() función que se llama desde la prueba de pasar en la función de devolución de llamada después de resolver desde el contenedor dentro de la prueba

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

La interfaz de devolución de llamada

public interface TestEventWrapperCallbackFunction { 

void CallbackOnEventFired(ApplicationEvent event); 
} 

Una clase de configuración de prueba para definir el bean al que se hace referencia en la prueba unitaria. Antes de que esto es útil, tendrá que ser resuelto desde el applicationContext y initialsed (ver paso siguiente)

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

Por último, la propia prueba de unidad que resuelve el grano de la applicationContext y llama a la Init() función para pasar los criterios de aserción (esto supone que ha registrado el bean como singleton, el valor predeterminado para Spring applicationContext). La función de devolución de llamada se define aquí y también pasó 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: El código de ejemplo se ha actualizado con CountDownLatch
  • EDIT 2: Las afirmaciones no dejó de pruebas de lo que lo anterior se ha actualizado con un enfoque diferente **
0

Puede crear un contexto manualmente.

Por ejemplo: yo necesitaba para comprobar si mi ApplicationListener<ContextClosedEvent> cerrado conexiones 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()); 
}