【问题标题】:How to integration test some Spring JMS config如何集成测试一些 Spring JMS 配置
【发布时间】:2014-05-06 05:20:03
【问题描述】:

我想知道是否有人可以提供帮助。我的 10 岁首发是我对 JMS 和一般消息传递知之甚少(几乎一无所知) - 所以请对我轻松回答任何问题/cmets :)

鉴于这对我来说是一个学习练习,我正在尝试整理一个非常基本的 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,所以我想知道在我的测试中是否有一种方法可以用 mock 替换 MessageListener bean,然后对其调用 verify。我想我可以通过为这些测试提供不同的 Spring 上下文文件来做到这一点,但这可能不适用于我的下一个需求......

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

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

考虑到上述情况,我认为我正在尝试做的是在运行时针对目标注册和取消注册(或禁用)侦听器,如果我能做到这一点,那么我可以注册模拟。

唷!,这很复杂!但我希望它对我正在尝试做的事情有意义?

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

内森

【问题讨论】:

标签: spring jms mockito jms-topic


【解决方案1】:

在我看来,一旦您进行集成测试,您就不应该尝试模拟任何东西。

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

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

您在这里的示例非常简单,也许这就是您感到困惑的原因(在我看来)。对于更复杂的侦听器,将其与系统的其余部分隔离会更自然。

【讨论】:

  • 感谢您的评论。也许我的术语在单元测试或集成测试方面是不正确的。我想我不想“测试”任何东西,因为它不是生产代码。这纯粹是我学习 JMS 的一个练习,一个允许我启动 Spring 上下文、触发消息并断言它们已被接收的测试类似乎是正确的做法。我想我称它为集成测试,因为我使用的是 SpringJUnit4ClassRunner,正如你所说的,它适合集成测试。我只是想弄清楚这些东西是如何协同工作的:)
  • @NathanRussell,好的,我明白了。那么也许你可以使用spring-mockito 创建一个模拟 JmsMessageListener 并使用它来代替你的 jmsListener。然后,您可以将其注入您的测试,并像往常一样使用 Mockito.verify。
猜你喜欢
  • 2015-05-19
  • 1970-01-01
  • 2012-06-14
  • 1970-01-01
  • 1970-01-01
  • 2021-01-24
  • 2013-04-19
  • 1970-01-01
  • 2013-12-31
相关资源
最近更新 更多