测试Spring onApplicationEvent的简单方法

时间:2017-04-20 18:35:20

标签: java spring unit-testing junit event-listener

我有一个包含函数的应用程序事件监听器:

@Override
public void onApplicationEvent(ContextRefreshedEvent event) {
//do some stuff
}

如何编写单元测试来模拟ContextRefreshedEvent,就像我的jar被执行一样,并测试我的onApplicationEvent函数是否完成了它应该做的事情?

2 个答案:

答案 0 :(得分:3)

这是我能想到的最小的独立示例。

import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ApplicationListener;
import org.springframework.context.annotation.Bean;
import org.springframework.context.event.ContextRefreshedEvent;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;

import java.util.concurrent.LinkedBlockingQueue;

import static org.junit.Assert.assertEquals;

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(classes = {RefreshTest.MyConfig.class})
public class RefreshTest {

    @Autowired
    private MyListener listener;

    @Test
    public void test() {
        assertEquals("Refresh should be called once",1, listener.events.size());
    }

    public static class MyConfig {
        @Bean
        public MyListener listener() {
            return new MyListener();
        }
    }

    public static class MyListener implements ApplicationListener <ContextRefreshedEvent> {
        // you don't really need a threadsafe collection in a test, as the test main thread is also loading the spring contest and calling the handler, 
        // but if you are inside an application, you should be aware of which thread is calling in case you want to read the result from another thread. 
        LinkedBlockingQueue<ContextRefreshedEvent> events = new LinkedBlockingQueue<ContextRefreshedEvent>();
        public void onApplicationEvent(ContextRefreshedEvent  event) {
            events.add(event);
        }
    }
}

测试处理程序中的代码与调用处理程序之间存在差异。不要将代码直接放在处理程序中,而是放在另一个bean中,这是从处理程序调用的,因此您可以在没有处理程序的情况下测试逻辑(通过使用您创建的ContextRefreshedEvent调用它)。刷新上下文时(通常在加载上下文时)会发送刷新事件,因此无需对其进行测试。如果在您的生产代码中没有调用它,您通常会立即注意到。测试和生产之间的上下文加载也可能不同,所以即使你编写了一个显示处理程序被调用的测试,也不能保证它会在生产中被调用,除非你运行完全相同的@Configuration - 我几乎从不这样做,因为我经常最终使用@Profile对某些配置/ bean进行不同的实现,例如当我不希望我的测试使用AWS队列和其他外部IO通道时。 / p>

答案 1 :(得分:0)

程序化有效负载监听器模拟测试

我基于这个 spring 启动示例实现了一个编程负载侦听器测试,如果可能有人需要模拟测试而不是加载所有 spring 上下文。

@Test
public void testProgrammaticEventListener() {
    final List<StoreEvent> events = new ArrayList<>();
    final ApplicationListener<PayloadApplicationEvent<Integer>> listener = forPayload(events::add);

    ConfigurableApplicationContext ac = new GenericApplicationContext();
    ac.addApplicationListener(listener);
    ac.refresh();

    PayloadApplicationEvent<Integer> event = new PayloadApplicationEvent<>(this, 1);
    ac.publishEvent(event);
    assertThat(events.contains(event.getPayload()), Is.is(true));
}

在示例代码中,ApplicationListener.forPayload中有一个方法;如果它不存在,您可以手动添加到您的测试类:

static <T> ApplicationListener<PayloadApplicationEvent<T>> forPayload(final Consumer<T> consumer) {
        return event -> consumer.accept(event.getPayload());
    }

参考 PayloadApplicationEventTests.java#L65