我有一个与height
- JMS
和Spring Boot
相关的标准设置。它工作正常,直到我尝试进行简单的集成测试。经过一些调查后,我发现在第一个JMS消息被消耗后,Spring上下文和嵌入式代理都被关闭,无论消费过程中是什么,都会触发另一个事件。我可以通过在测试设置中添加ActiveMQ
连接选项来解决代理问题,即
useShutdownHook=false
我正在寻找的东西基本上是一种迫使测试“保持活力”的方法。直到消耗掉所有JMS消息(在这种情况下它们只是两个)。我理解整个设置的异步性质,但是在测试期间,生成和使用这些消息的所有结果会很有帮助。
以下是我的设置,虽然它相当简单。
spring.activemq.broker-url = vm://broker?async=false&broker.persistent=false&broker.useShutdownHook=false
然后我有一个消息驱动的POJO来监听给定的事件:
@EnableJms
public class ActiveMqConfig {
@Bean
public JmsTemplate jmsTemplate(ConnectionFactory connectionFactory, MessageConverter messageConverter) {
JmsTemplate jmsTemplate = new JmsTemplate(connectionFactory);
jmsTemplate.setMessageConverter(messageConverter);
return jmsTemplate;
}
@Bean
public MessageConverter messageConverter() {
MappingJackson2MessageConverter messageConverter = new MappingJackson2MessageConverter();
messageConverter.setTargetType(MessageType.TEXT);
messageConverter.setTypeIdPropertyName("_type");
return messageConverter;
}
}
还有一个:
@JmsListener(destination = "events")
public void applicationSubmitted(MyType event) {
// do some work with the event here
jmsTemplate.convertAndSend("commands", mymessage);
}
我试过的一件事就是在邮件发送后添加延迟,即@JmsListener(destination = "commands")
public void onCommand(TextMessage textMessage) {
}
。然而,这非常不可靠并且还会减慢测试速度,因为执行可能需要不到50毫秒。以下是测试本身。除非等待取消注释,否则我永远不会进入第二个事件监听器,因为应用程序上下文关闭,测试结束,消息被“忘记”#34;。
sleep(200)
答案 0 :(得分:3)
嗯,这是基于异步消息交换测试系统时的标准问题。通常,它会在您跳过的测试部分中解决 - then
部分。
问题在于,在您的测试中,您通常希望系统做一些有用的事情,例如:在DB中进行更改,向另一个系统发送一个休息调用,在另一个队列中发送消息等。我们可以等待一段时间直到它发生,不断检查结果 - 如果结果是在我们的时间窗口内实现的已经设定 - 然后我们可以假设测试已经过去了。
这种方法的伪代码如下:
for (i to MAX_RETRIES; i++) {
checkThatTheChangesInDBHasBeenMade();
checkThatTheRestCallHasBeenMade();
checkThatTheMessageIsPostedInAnotherQueue();
Thread.sleep(50ms);
}
这种方式在最佳情况下,您的测试将在50ms内完成。在更糟糕的情况下,它将失败,这将花费MAX_RETRIES * 50ms的时间来执行测试。
另外,我应该提一下,有一个很好的工具叫awaitility提供了很好的API(顺便说一句,它支持groovy DSL)来处理异步世界中的这类问题:
await().atMost(5, SECONDS).until(customerStatusIsUpdated());
答案 1 :(得分:1)
我认为问题的根源是异步事件处理。发送活动后,您的测试即将结束。这当然会导致Spring上下文和代理关闭。 JMS侦听器正在另一个线程中运行。你必须找到一种等待它们的方法。否则,您的线程(这是您的测试用例)就完成了。
我们在上一个项目中遇到了类似的问题并编写了一个小实用程序,它帮助了我们很多。 JMS提供了“浏览”队列并查看它是否为空的能力:
public final class JmsUtil {
private static final int MAX_TRIES = 5000;
private final JmsTemplate jmsTemplate;
public JmsUtil(JmsTemplate jmsTemplate) {
this.jmsTemplate = jmsTemplate;
}
private int getMessageCount(String queueName) {
return jmsTemplate.browseSelected(queueName, "true = true", (s, qb) -> Collections.list(qb.getEnumeration()).size());
}
public void waitForAll(String queueName) {
int i = 0;
while (i <= MAX_TRIES) {
if (getMessageCount(queueName) == 0) {
return;
}
i++;
}
}
使用此实用程序,您可以执行以下操作:
def "My event is successfully handled"() {
given:
def event = new MyEvent()
when:
jmsTemplate.convertAndSend("events", event)
jmsUtility.waitForAll("events"); // wait until the event has been consumed
jmsUtility.waitForAll("commands"); // wait until the command has been consumed
then:
1 == 1
}
注意:此实用程序假定您将JMS消息发送到队列。通过浏览队列,我们可以检查它是否为空。如果是某个主题,您可能需要进行另一项检查。所以要注意这一点!