SpringBoot / RabbitMq集成测试不再适用于不同的环境

时间:2017-06-19 05:10:35

标签: java spring-boot rabbitmq spring-rabbit spring-rabbitmq

我有一个在Windows Jenkins从站和本地(在Windows上)运行良好的测试套件,我们现在正在Docker Linux映像上迁移我们的Jenkins slave。

很好的构建在新的slave上运行不正常,我们在集成测试中看到失败,特别是那些期望从RabbitMq接收一些消息的。 Java和Maven版本在新的Linux奴隶上可能略有不同,但我不明白为什么在这个新环境中已经运行好几个月的东西已经不再适用了。

我觉得调试很困难,而且我不确定从哪里开始,因为它在我的机器上工作..

有没有人已经面对这种行为并且可以建议指针?

1 个答案:

答案 0 :(得分:2)

这就是我解决问题的方法:我不想在我的测试中添加太多Thread.sleep(),以便我可以在RabbitMq UI上实时检查是否可以看到消息经过并被消耗...所以我添加了更多日志。

我很快发现该消息是由听众使用的,即使我的测试失败了。我的测试试图检查在处理过程中出现故障时,消息被发送到deadletter queue。所以我在deadletter queue上有一个监听器。以下是我在日志中可以看到的内容的快速摘要:

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

我做了两件事:

  1. 确保在我的构建期间队列/交换是唯一的
  2. 我怀疑我可能有多个消费者在队列中,因为其他一些工作并行运行并连接在相同的交换/队列上。所以我想确保每次运行时名称都是唯一的。

    关注Look up hostname from Maven,这就是我所做的:

    • 配置Maven构建以获取对主机名的访问权限并稍后通过此Maven插件配置注入(下面的配置应包含在插件 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
      

    - &GT;我可以非常清楚地看到我正在断言的监听器的引用和收到消息的监听器的引用是不同的!显然,这就是我的测试失败的原因。

    - &GT;在我的测试套件中的某个地方,另一个监听器仍处于活动状态并正在使用该消息。

    1. 清楚地确定哪些消费者获得了消息
    2. 为此,我在创建时清楚地记录了侦听器的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版本的细微差别如何产生这种影响? - 为什么前一次测试的听众仍在运行,请求测试正确完成? - 我真的必须在所有加载Spring上下文的集成测试中加上@DirtiesContext吗?

      如果有人对此有一些答案,我会感兴趣。