SpringBoot / RabbitMq 集成测试不再适用于不同的环境
SpringBoot / RabbitMq integration tests not working anymore on different environment
我有一个测试套件 运行 在 Windows Jenkins 从站和本地(在 Windows 上)都很好,我们现在正在 [=21= 上迁移我们的 Jenkins 从站] Linux 图片。
好的构建在新的从站上 运行 不正确,我们看到集成测试失败,尤其是那些期望从 RabbitMq 接收一些消息的测试。
Java 和 Maven 版本在新的 Linux slave 上可能略有不同,但我不明白为什么 运行 好几个月的东西在新环境中不再存在。
我发现调试起来非常困难,而且我不知道从哪里开始,因为它在我的机器上运行..
有没有人已经遇到过这种行为并可以提出建议?
这就是我解决问题的方法:我不想在我的测试中添加太多 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
我做了两件事:
- 确保 queues/exchanges 在我的构建过程中是唯一的
我怀疑队列中可能有多个消费者,因为一些其他作业 运行 并行并连接到相同的交换/队列。所以我想确保每个 运行 的名称都是唯一的。
在 Look up hostname from Maven 之后,这就是我所做的:
配置 Maven 构建以访问主机名并稍后注入它,通过此 Maven 插件配置(以下配置应包装在 plugin XML 元素,但出于某种原因,Whosebug 不接受它):
<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@
这样,queues/exchanges 就这样创建了:
DEBUG RabbitAdmin - declaring Exchange 'myApp.dead.letter.exchange-d11525d215a8'
DEBUG RabbitAdmin - declaring Queue 'myApp.dead.letter.queue-d11525d215a8'
因为对于每个作业,Docker 都会生成一个新的 "machine",主机名每次都不同。但尽管如此,它仍然无法正常工作。
我在测试和监听器中添加了更多日志,因为我有一个疑问:可以解释这个问题的唯一原因是我没有断言我应该反对监听器。我可以在日志中看到:
在我的测试中:
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
我确认来自先前测试的 "zombie listener" 仍然存在并使用消息,而不是我在测试中创建的侦听器。我检查了第一个测试 (RequestCodeGeneratorServiceImplIT),并注意到 它没有 @DirtiesContext 注释 。我添加它是为了确保一切都已清理干净,现在我的测试套件通过了!
尽管我找到了问题的解决方案,但仍有几件事我不清楚:
- java/maven 版本中的微小差异如何产生这种影响?
- 尽管测试正确完成,但之前测试的侦听器为何仍然可用并且 运行ning?
- 我真的必须将@DirtiesContext 放在加载Spring 上下文的所有集成测试上吗?
如果有人对此有一些答案,我会很感兴趣。
我有一个测试套件 运行 在 Windows Jenkins 从站和本地(在 Windows 上)都很好,我们现在正在 [=21= 上迁移我们的 Jenkins 从站] Linux 图片。
好的构建在新的从站上 运行 不正确,我们看到集成测试失败,尤其是那些期望从 RabbitMq 接收一些消息的测试。 Java 和 Maven 版本在新的 Linux slave 上可能略有不同,但我不明白为什么 运行 好几个月的东西在新环境中不再存在。
我发现调试起来非常困难,而且我不知道从哪里开始,因为它在我的机器上运行..
有没有人已经遇到过这种行为并可以提出建议?
这就是我解决问题的方法:我不想在我的测试中添加太多 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
我做了两件事:
- 确保 queues/exchanges 在我的构建过程中是唯一的
我怀疑队列中可能有多个消费者,因为一些其他作业 运行 并行并连接到相同的交换/队列。所以我想确保每个 运行 的名称都是唯一的。
在 Look up hostname from Maven 之后,这就是我所做的:
配置 Maven 构建以访问主机名并稍后注入它,通过此 Maven 插件配置(以下配置应包装在 plugin XML 元素,但出于某种原因,Whosebug 不接受它):
<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@
这样,queues/exchanges 就这样创建了:
DEBUG RabbitAdmin - declaring Exchange 'myApp.dead.letter.exchange-d11525d215a8'
DEBUG RabbitAdmin - declaring Queue 'myApp.dead.letter.queue-d11525d215a8'
因为对于每个作业,Docker 都会生成一个新的 "machine",主机名每次都不同。但尽管如此,它仍然无法正常工作。 我在测试和监听器中添加了更多日志,因为我有一个疑问:可以解释这个问题的唯一原因是我没有断言我应该反对监听器。我可以在日志中看到:
在我的测试中:
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
我确认来自先前测试的 "zombie listener" 仍然存在并使用消息,而不是我在测试中创建的侦听器。我检查了第一个测试 (RequestCodeGeneratorServiceImplIT),并注意到 它没有 @DirtiesContext 注释 。我添加它是为了确保一切都已清理干净,现在我的测试套件通过了!
尽管我找到了问题的解决方案,但仍有几件事我不清楚: - java/maven 版本中的微小差异如何产生这种影响? - 尽管测试正确完成,但之前测试的侦听器为何仍然可用并且 运行ning? - 我真的必须将@DirtiesContext 放在加载Spring 上下文的所有集成测试上吗?
如果有人对此有一些答案,我会很感兴趣。