【问题标题】:spring-integration unit test outbound-channel adapter弹簧集成单元测试出站通道适配器
【发布时间】:2013-04-23 01:13:12
【问题描述】:

我有以下配置

<int:channel id="notificationChannel" datatype="com.mycompany.integration.NotificationMessage">
        <int:queue message-store="jdbc-message-store" capacity="1000" />
    </int:channel>

    <int:outbound-channel-adapter ref="notificationHandler"
        method="handle" channel="notificationChannel" >
        <int:poller max-messages-per-poll="100" fixed-delay="60000"
            time-unit="MILLISECONDS" >
            <int:transactional isolation="DEFAULT" />
        </int:poller>
    </int:outbound-channel-adapter>

现在我想对此进行单元测试,我需要等待消息在测试中被正确处理,我尝试使用拦截器但它不起作用,因为我只能同步消息传递但不能成功消息的处理。在处理完成后实现发送回复,但这意味着实现这个只是为了使我的单元测试工作,在生产中不会在消息头中设置回复通道。我怎样才能在成功处理请求时实现同步而不在 messageHandler 中实现它?

【问题讨论】:

    标签: spring unit-testing spring-integration


    【解决方案1】:

    如果您使用的是 Spring Integration 2.2.x,您可以通过建议来做到这一点...

    public class CompletionAdvice extends AbstractRequestHandlerAdvice {
    
        private final CountDownLatch latch = new CountDownLatch(1);
    
        @Override
        protected Object doInvoke(ExecutionCallback callback, Object target, Message<?> message) throws Exception {
            Object result = callback.execute();
            latch.countDown();
            return result;
        }
    
        public CountDownLatch getLatch() {
            return latch;
        }
    
    }
    

    在您的测试环境中,使用 bean 工厂后处理器将建议添加到适配器的处理程序。

    public class AddCompletionAdvice implements BeanFactoryPostProcessor {
    
        private final Collection<String> handlers;
    
        private final Collection<String> replyProducingHandlers;
    
        public AddCompletionAdvice(Collection<String> handlers, Collection<String> replyProducingHandlers) {
            this.handlers = handlers;
            this.replyProducingHandlers = replyProducingHandlers;
        }
    
        @Override
        public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {
            for (String beanName : handlers) {
                defineAdviceAndInject(beanFactory, beanName, beanName + "CompletionAdvice");
            }
            for (String beanName : replyProducingHandlers) {
                String handlerBeanName = beanFactory.getAliases(beanName + ".handler")[0];
                defineAdviceAndInject(beanFactory, handlerBeanName, beanName + "CompletionAdvice");
            }
        }
    
        private void defineAdviceAndInject(ConfigurableListableBeanFactory beanFactory, String beanName, String adviceBeanName) {
            BeanDefinition serviceHandler = beanFactory.getBeanDefinition(beanName);
            BeanDefinition advice = new RootBeanDefinition(CompletionAdvice.class);
            ((BeanDefinitionRegistry) beanFactory).registerBeanDefinition(adviceBeanName, advice);
            serviceHandler.getPropertyValues().add("adviceChain", new RuntimeBeanReference(adviceBeanName));
        }
    
    }
    

    将后处理器添加到配置&lt;bean class="foo.AddCompletionAdvice" /&gt;

    最后,将建议注入您的测试用例

    @ContextConfiguration
    @RunWith(SpringJUnit4ClassRunner.class)
    public class TestAdvice {
    
        @Autowired
        private CompletionAdvice fooCompletionAdvice;
    
        @Autowired
        private CompletionAdvice barCompletionAdvice;
    
        @Autowired
        private MessageChannel input;
    
        @Test
        public void test() throws Exception {
            Message<?> message = new GenericMessage<String>("Hello, world!");
            input.send(message);
            assertTrue(fooCompletionAdvice.getLatch().await(1, TimeUnit.SECONDS));
            assertTrue(barCompletionAdvice.getLatch().await(1, TimeUnit.SECONDS));
        }
    
    }
    

    并等待闩锁。

    <int:publish-subscribe-channel id="input"/>
    
    <int:outbound-channel-adapter id="foo" channel="input" ref="x" method="handle"/>
    
    <int:service-activator id="bar" input-channel="input" ref="x"/>
    
    <bean class="foo.AddCompletionAdvice">
        <constructor-arg name="handlers">
            <list>
                <value>foo</value>
            </list>
        </constructor-arg>
        <constructor-arg name="replyProducingHandlers">
            <list>
                <value>bar</value>
            </list>
        </constructor-arg>
    </bean>
    
    <bean id="x" class="foo.Foo" />
    

    我将这些类添加到Gist

    编辑:更新为最终消费者(无回复)和回复生产消费者提供一般案例。

    【讨论】:

    • 这看起来很棒。我会尽快将其标记为解决方案,谢谢。
    • 我已经为适配器设置了一个 ID 并使用了您的代码。现在我得到原因:org.springframework.beans.NotWritablePropertyException:bean 类 [org.springframework.integration.handler.MethodInvokingMessageHandler] 的无效属性“adviceChain”:Bean 属性“adviceChain”不可写或设置方法无效。 setter的参数类型和getter的返回类型是否匹配?
    • 我尝试在轮询器而不是具有此属性的通道适配器上设置建议链,但没有调用完成建议
    • 抱歉,我使用返回 null 的 &lt;service-activator/&gt; 对其进行了测试。我编辑了帖子以展示如何建议出站适配器。
    • 对不起 - 我的错误 - 我实际上没有时间测试它;我们需要注入带有通知名称的RuntimeBeanReference,而不是BeanDefinition。我更新了答案。当然,您也可以在上下文中定义通知并将其注入到后处理器和测试用例中(而不是创建新的 bean 定义。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2015-07-11
    • 1970-01-01
    • 2014-12-07
    • 1970-01-01
    • 1970-01-01
    • 2021-12-29
    相关资源
    最近更新 更多