【问题标题】:junit spring jms listenerjunit spring jms 监听器
【发布时间】:2017-12-30 10:38:44
【问题描述】:

我想在下面对一个简单的 jms 监听器代码进行单元测试

@Component
public class NotificationReader {

    @JmsListener(destination = "myAppQ")
    public void receiveMessage(NotificationMessage notificationMessage) {
        System.out.println("Received <" + notificationMessage.getStaffNumber() + ">");
    }

}

在 junit 中,我使用 jmsTemplate 将消息泵入 Active MQ。

我想测试 jms 监听器是否被调用。

我看到很少有解决方案(使用计数器),例如 How to wait for @JMSListener annotated method to complete in JUnit,它实际上只是为了测试目的而更改了侦听器代码,而我不想这样做。

还有其他选择吗?


按照答案中的建议尝试配置。

@RunWith(SpringRunner.class)
@SpringBootTest
public class TestNotificationReader {

    @Autowired
    JmsTemplate jmsTemplate;

    @Value("${outbound.endpoint}")
    private String destination;

    @Test
    public void contextLoads() {
    }

    @Test
    public void testPutToQ() {
        NotificationMessage notificationMessage = new NotificationMessage();
        notificationMessage.setStaffNumber("100");
        notificationMessage.setWorkflowType("TYPE");
        notificationMessage.setWorkflowId("100");
        notificationMessage.setWorkflowDescription("Test From Queue");
        jmsTemplate.convertAndSend(destination, notificationMessage);

        jmsTemplate.setReceiveTimeout(10000);

        try {
            TestConfig.latch.await(10, TimeUnit.SECONDS);

            NotificationMessage temp = (NotificationMessage) TestConfig.received;

            System.out.println(" temp.getStaffNumber() " + temp.getStaffNumber());
        } catch (InterruptedException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
    }

    @Configuration
    public static class TestConfig {

        private static final CountDownLatch latch = new CountDownLatch(1);

        private static Object received;

        @Bean
        public static BeanPostProcessor listenerWrapper() {
            return new BeanPostProcessor() {

                @Override
                public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
                    if (bean instanceof NotificationReader) {
                        MethodInterceptor interceptor = new MethodInterceptor() {

                            @Override
                            public Object invoke(MethodInvocation invocation) throws Throwable {
                                Object result = invocation.proceed();
                                if (invocation.getMethod().getName().equals("receiveMessage")) {
                                    received = invocation.getArguments()[0];
                                    latch.countDown();
                                }
                                return result;
                            }

                        };
                        if (AopUtils.isAopProxy(bean)) {
                            ((Advised) bean).addAdvice(interceptor);
                            return bean;
                        } else {
                            ProxyFactory proxyFactory = new ProxyFactory(bean);
                            proxyFactory.addAdvice(interceptor);
                            return proxyFactory.getProxy();
                        }
                    } else {
                        return bean;
                    }
                }

                @Override
                public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
                    // TODO Auto-generated method stub
                    return bean;
                }

            };
        }

    }

}

添加 testConfig 后,JMSTempate 自动装配失败

Caused by: org.springframework.beans.factory.NoSuchBeanDefinitionException: No qualifying bean of type 'org.springframework.jms.core.JmsTemplate' available: expected at least 1 bean which qualifies as autowire candidate. Dependency annotations: {@org.springframework.beans.factory.annotation.Autowired(required=true)}
    at org.springframework.beans.factory.support.DefaultListableBeanFactory.raiseNoMatchingBeanFound(DefaultListableBeanFactory.java:1493) ~[spring-beans-4.3.13.RELEASE.jar:4.3.13.RELEASE]

【问题讨论】:

    标签: spring-jms spring-junit


    【解决方案1】:

    将您的侦听器 bean 包装在代理中(用于测试用例)并使用锁存器并验证接收到的对象是否符合您的预期...

    @RunWith(SpringRunner.class)
    @SpringBootTest(classes = { So48033124Application.class, So48033124ApplicationTests.TestConfig.class })
    public class So48033124ApplicationTests {
    
        @Autowired
        private JmsTemplate template;
    
        @Test
        public void test() throws Exception {
            Foo foo = new Foo("bar");
            this.template.convertAndSend("foo", foo);
            assertThat(TestConfig.latch.await(10, TimeUnit.SECONDS)).isTrue();
            assertThat(TestConfig.received).isEqualTo(foo);
        }
    
        @Configuration
        public static class TestConfig {
    
            private static final CountDownLatch latch = new CountDownLatch(1);
    
            private static Object received;
    
            @Bean
            public static BeanPostProcessor listenerWrapper() {
                return new BeanPostProcessor() {
    
                    @Override
                    public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
                        if (bean instanceof MyListener) {
                            MethodInterceptor interceptor = new MethodInterceptor() {
    
                                @Override
                                public Object invoke(MethodInvocation invocation) throws Throwable {
                                    Object result = invocation.proceed();
                                    if (invocation.getMethod().getName().equals("listen")) {
                                        received = invocation.getArguments()[0];
                                        latch.countDown();
                                    }
                                    return result;
                                }
    
                            };
                            if (AopUtils.isAopProxy(bean)) {
                                ((Advised) bean).addAdvice(interceptor);
                                return bean;
                            }
                            else {
                                ProxyFactory proxyFactory = new ProxyFactory(bean);
                                proxyFactory.addAdvice(interceptor);
                                return proxyFactory.getProxy();
                            }
                        }
                        else {
                            return bean;
                        }
                    }
    
                };
            }
    
        }
    
    }
    

    编辑

    以上基于 Spring Framework 5 或更高版本,使用 Java 8 并为 BeanPostProcessor 方法提供默认实现。

    如果您使用的是 Spring 的早期版本,您还需要

    @Override
    public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
        return bean;
    }
    

    BPP 也应该是static

    【讨论】:

    • 我无法编译这段代码,因为 BeanPostProcessor() 必须实现 postProcessBeforeInitialization 。使用此配置后,由于现有 bean 的自动装配失败,上下文加载失败。请检查除了上面提供的代码之外是否需要任何其他代码
    • 示例基于Spring 5.0;看我的编辑。你应该不需要更多了;您还应该声明 BPP static 以确保它在您的侦听器 bean 之前注册。如果您仍然无法使用它,请编辑您的问题以显示 COMPLETE 配置和测试。
    • 感谢您的回复 Gary Russel。我仍然无法让它工作。我已经发布了我的 junit 测试代码
    • 当你为你的测试添加一个配置类时;您必须在 @SpringBootTest 中配置主应用程序 - 请参阅我的示例:@SpringBootTest(classes = { So48033124Application.class, So48033124ApplicationTests.TestConfig.class })
    • 现在工作正常。感谢您提供有关如何在测试用例中使用配置类的详细信息
    猜你喜欢
    • 1970-01-01
    • 2023-03-26
    • 2017-01-23
    • 1970-01-01
    • 2015-02-28
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2016-03-07
    相关资源
    最近更新 更多