这就是我解决问题的方法:我不想在测试中添加太多 Thread.sleep() 以允许我在 RabbitMq UI 上实时检查是否可以看到消息通过并被消耗...所以我添加了更多日志。
我很快发现该消息已被侦听器使用,即使我的测试失败了。我的测试试图检查当处理过程中出现故障时,消息被发送到死信队列。所以我在死信队列上有一个监听器。以下是我在日志中可以看到的内容的快速摘要:
18:51:05 2017-06-16 18:51:04,907 [main] DEBUG KickOffEventListenerIT - sent message !!
...
18:51:05 2017-06-16 18:51:04,970 [SimpleAsyncTaskExecutor-1] INFO DeadLetterQueueListener - received a message on deadletter queue
但我的测试失败了:
deadLetterQueueListener.getLatch().await(1000, TimeUnit.MILLISECONDS);
assertThat(deadLetterQueueListener.getReceivedMessages()).isNotEmpty();
正在给予:
java.lang.AssertionError: Expecting actual not to be empty
我做了两件事:
- 在我的构建过程中确保队列/交换是唯一的
我怀疑队列中可能有多个消费者,因为其他一些作业并行运行并连接在相同的交换/队列上。所以我想确保每次运行的名称都是唯一的。
按照Look up hostname from Maven,这就是我所做的:
-
配置 Maven 构建以访问主机名并稍后通过此 Maven 插件配置将其注入(以下配置应包装在 plugin XML 元素中,但由于某种原因,stackoverflow 不接受它):
<groupId>org.codehaus.gmaven</groupId>
<artifactId>gmaven-plugin</artifactId>
<version>1.5</version>
<executions>
<execution>
<phase>generate-resources</phase>
<goals>
<goal>execute</goal>
</goals>
<configuration>
<providerSelection>2.0</providerSelection>
<source>
project.properties["hostname"] = InetAddress.getLocalHost().getHostName()
</source>
</configuration>
</execution>
</executions>
并像这样配置我的应用程序:
myApp.messaging:
dead.letter:
exchange:
name: myApp.dead.letter.exchange-@hostname@
queue:
name: myApp.dead.letter.queue-@hostname@
这样,队列/交换会像这样创建:
DEBUG RabbitAdmin - declaring Exchange 'myApp.dead.letter.exchange-d11525d215a8'
DEBUG RabbitAdmin - declaring Queue 'myApp.dead.letter.queue-d11525d215a8'
因为对于每个作业,Docker 都会旋转一个新的“机器”,主机名每次都不同。但尽管如此,它仍然无法正常工作。
我在测试和监听器中添加了更多日志,因为我有一个疑问:可以解释这个问题的唯一原因是我没有断言我应该反对监听器。我可以在日志中看到:
-
在我的测试中:
DEBUG KickOffEventListenerIT - sent message !!
DEBUG KickOffEventListenerIT - waiting infos from deadLetterQueueListener myApp.remote.service.mocks.DeadLetterQueueListener@37093884
DEBUG KickOffEventListenerIT - waiting infos from latch java.util.concurrent.CountDownLatch@7e2e2d7d[Count = 1]
-
在监听器中:
DEBUG DeadLetterQueueListener - processing infos from deadLetterQueueListener myApp.remote.service.mocks.DeadLetterQueueListener@2142ab69
DEBUG DeadLetterQueueListener - processing infos from latchjava.util.concurrent.CountDownLatch@79f8793b[Count = 1]
INFO DeadLetterQueueListener - deadletter queue received message size : 1
--> 我可以非常清楚地看到,我所断言的听众的引用和获取消息的听众的引用是不同的!显然,这就是我的测试失败的原因。
--> 在我的测试套件的某个地方,另一个侦听器仍然处于活动状态并正在使用该消息。
- 清楚地确定收到消息的消费者
为此,我在创建时清楚地记录了侦听器的 ID:
@Component
@Slf4j
public class DeadLetterQueueListener {
@PostConstruct
public void logReferenceId(){
log.debug("just built deadLetterQueueListener : "+this);
}
...
}
然后通过查看日志中的对象引用就很明显了:
19:24:21 Running myApp.service.impl.RequestCodeGeneratorServiceImplIT
19:24:23 2017-06-16 19:24:22,907 [main] DEBUG DeadLetterQueueListener - just built deadLetterQueueListener : myApp.remote.service.mocks.DeadLetterQueueListener@58984698
19:24:28 Tests run: 1, Failures: 0, Errors: 0, Skipped: 0, Time elapsed: 6.058 sec - in myApp.service.impl.RequestCodeGeneratorServiceImplIT
19:24:29 Running myApp.messaging.incoming.KickOffEventListenerIT
19:24:30 2017-06-16 19:24:29,913 [main] DEBUG DeadLetterQueueListener - just built deadLetterQueueListener : myApp.remote.service.mocks.DeadLetterQueueListener@32096336
19:24:31 2017-06-16 19:24:31,187 [main] DEBUG KickOffEventListenerIT - waiting infos from deadLetterQueueListener myApp.remote.service.mocks.DeadLetterQueueListener@32096336
19:24:31 2017-06-16 19:24:31,250 [SimpleAsyncTaskExecutor-1] DEBUG DeadLetterQueueListener - processing infos from deadLetterQueueListener myApp.remote.service.mocks.DeadLetterQueueListener@58984698
我确认之前测试中的“僵尸侦听器”仍然存在并正在使用消息,而不是我在测试中创建的侦听器。我检查了第一个测试 (RequestCodeGeneratorServiceImplIT),并注意到 它没有 @DirtiesContext 注释。我添加它以确保所有内容都已清理,现在我的测试套件通过了!
尽管我找到了解决问题的方法,但我仍然不清楚一些事情:
- java/maven 版本的微小差异如何产生这种影响?
- 为什么上一个测试中的侦听器仍然启动并运行,尽管测试正确完成?
- 我真的必须将@DirtiesContext 放在我所有加载Spring 上下文的集成测试中吗?
如果有人对此有一些答案,我会很感兴趣。