如何集成测试一些Spring JMS配置

时间:2014-03-27 10:04:23

标签: spring jms mockito jms-topic

我想知道是否有人可以提供帮助。我对10的首发是我对JMS和一般情况下的消息知之甚少(几乎没有) - 所以请随身携带任何答案/评论:)

鉴于这对我来说是一个学习方法,我试图整理一个非常基本的Spring JMS配置,然后编写一些集成测试来帮助我理解它是如何工作的。

这是我当前的Spring上下文配置及其JMS组件:

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:p="http://www.springframework.org/schema/p"
       xmlns:context="http://www.springframework.org/schema/context"
       xmlns:jms="http://www.springframework.org/schema/jms"
       xmlns:amq="http://activemq.apache.org/schema/core"
       xsi:schemaLocation="http://www.springframework.org/schema/beans
                           http://www.springframework.org/schema/beans/spring-beans.xsd
                           http://www.springframework.org/schema/context
                           http://www.springframework.org/schema/context/spring-context.xsd
                           http://www.springframework.org/schema/jms
                           http://www.springframework.org/schema/jms/spring-jms.xsd
                           http://activemq.apache.org/schema/core
                           http://activemq.apache.org/schema/core/activemq-core.xsd">

    <bean class="com.lv.gi.jmstest.ApplicationContextProvider" />

    <!--  Embedded ActiveMQ Broker -->
    <amq:broker id="broker" useJmx="false" persistent="false">
        <amq:transportConnectors>
            <amq:transportConnector uri="tcp://localhost:0" />
        </amq:transportConnectors>
    </amq:broker>

    <!--  ActiveMQ Destination  -->
    <amq:queue id="destination" physicalName="myQueueName" />

    <!-- JMS ConnectionFactory to use, configuring the embedded broker using XML -->
    <amq:connectionFactory id="jmsFactory" brokerURL="vm://localhost" />

    <!-- JMS Producer Configuration -->
    <bean id="jmsProducerConnectionFactory"
          class="org.springframework.jms.connection.SingleConnectionFactory"
          depends-on="broker"
          p:targetConnectionFactory-ref="jmsFactory" />

    <bean id="jmsProducerTemplate" class="org.springframework.jms.core.JmsTemplate"
          p:connectionFactory-ref="jmsProducerConnectionFactory"
          p:defaultDestination-ref="destination" />

    <bean class="com.lv.gi.jmstest.JmsMessageProducer">
        <constructor-arg index="0" ref="jmsProducerTemplate" />
    </bean>

    <!-- JMS Consumer Configuration -->
    <bean id="jmsConsumerConnectionFactory"
          class="org.springframework.jms.connection.SingleConnectionFactory"
          depends-on="broker"
          p:targetConnectionFactory-ref="jmsFactory" />

    <jms:listener-container container-type="default"
                            connection-factory="jmsConsumerConnectionFactory"
                            acknowledge="auto">
        <jms:listener destination="myQueueName" ref="jmsMessageListener" />
    </jms:listener-container>

    <bean id="jmsMessageListener" class="com.lv.gi.jmstest.JmsMessageListener" />
</beans>

我的JmsMessageProducer类有一个postMessage方法,如下所示:

public void postMessage(final String message) {
    template.send(new MessageCreator() {
        @Override
        public Message createMessage(final Session session) throws JMSException {
            final TextMessage textMessage = session.createTextMessage(message);
            LOGGER.info("Sending message: " + message);

            return textMessage;
        }
    });
}

我的JmsMessageListener(实现MessageListener)有一个onMessage方法,如下所示:

public void onMessage(final Message message) {
    try {
        if (message instanceof TextMessage) {
            final TextMessage tm = (TextMessage)message;
            final String msg = tm.getText();

            LOGGER.info("Received message '{}'", msg);
        }
    } catch (final JMSException e) {
        LOGGER.error(e.getMessage(), e);
    }
}

在我的测试类中,我可以启动Spring上下文,获取JmsMessageProducer bean,并调用postMessage;我按预期在控制台上看到了消息:

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(locations = { "classpath:/com/lv/gi/jmstest/JmsMessageListenerTest-context.xml" })
public class TestJms {

    private JmsMessageProducer jmsMessageProducer;

    @Before
    public void setup() {
        jmsMessageProducer = ApplicationContextProvider.getApplicationContext().getBean(JmsMessageProducer.class);
    }

    @Test
    public void doStuff() throws InterruptedException {
        jmsMessageProducer.postMessage("message 1");
    }
}

虽然这有效,但它并不是真正的测试,因为除了我在视觉上看到控制台上收到的消息之外,我无法断言已收到消息。

我们使用mockito,所以我想知道我的测试中是否有一种方法可以用模拟替换MessageListener bean,然后在其上调用verify。我想我可以通过为这些测试提供不同的Spring上下文文件来做到这一点,但这可能不适合我的下一个要求......

我的最终目标是创建一个主题,我的消息生产者可以在其中向队列发送消息,并且一个或多个MessageListener将从队列中读取消息,并且所有已注册的侦听器都已读取消息,消息将从队列中删除。 (我认为主题是正确的术语!)

为了证明这个系统能够正常工作,我想要一个可以启动Spring上下文的测试。我要做的第一件事就是用3个模拟替换监听器,所有模拟都连接到同一个目的地,这样我就可以在每个模拟器上使用验证。我发布消息,然后验证每个模拟已收到。然后我想删除/禁用2个侦听器,调用postMessage,并验证在剩下的一个模拟侦听器上调用onMessage方法。然后或许等一会儿,重新建立2个模拟,并验证他们的onMessage方法被调用。最后,检查消息是否已不在队列中(由于3个已注册的侦听器都收到了消息)

考虑到上述情况,我我想要做的是在运行时注册和取消注册(或禁用)针对目标的侦听器,如果我能做到这一点,然后我可以注册嘲笑。

先生,这太复杂了!但我希望我能做的事情有意义吗?

关于如何实现这一点的任何指针?非常感谢,

1 个答案:

答案 0 :(得分:1)

在我看来,只要你进行集成测试,你就不应该试图模仿任何东西。

一方面你编写单元测试。例如,您可以通过直接从测试中调用jmsMessageListener的onMessage方法来测试使用者的行为。您通常不会使用SpringJUnit4ClassRunner进行此类测试,并且通常使用Mockito来模拟正在测试的对象的依赖关系。

另一方面,您进行了集成测试。这些是测试整个应用程序的行为。在这里使用SpringJUnit4ClassRunner.class是有意义的,但不是Mockito。你应该测试你的jmsListener应该做的事情。例如,如果您的应用程序应该写入有关传入消息的日志,请打开日志文件并进行检查。

这里的例子非常简单,也许这就是为什么你感到困惑(在我看来)。使用更复杂的侦听器,它与系统的其他部分隔离会更自然。