2

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

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

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

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

唷!,这很复杂!,但我希望这对我想要做什么有意义?

关于如何实现这一目标的任何指示?提前谢谢了,

弥敦道

4

1 回答 1

1

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

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

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

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

于 2014-03-27T11:26:07.307 回答