Интеграционные тесты SpringBoot / RabbitMq больше не работают в другой среде

У меня есть набор тестов, работающий нормально на подчиненном устройстве Windows Jenkins и локально (на Windows), и теперь мы переносим наше ведомое устройство Jenkins на образ Docker Linux.

Сборка, которая была в порядке, не работает должным образом на новом ведомом устройстве, где мы видим сбои в интеграционных тестах, особенно те, которые ожидают получения некоторых сообщений от RabbitMq. Версии Java и Maven могут немного отличаться на новом подчиненном Linux, но я не понимаю, почему что-то, что работало в течение нескольких месяцев, больше не работает в этой новой среде.

Я нахожу это довольно трудным для отладки, и я не уверен, с чего начать, так как он работает на моей машине..

Кто-нибудь уже сталкивался с таким поведением и мог бы предложить указатели?

1 ответ

Решение

Вот как я решил эту проблему: я не хотел добавлять слишком много Thread.sleep() в свой тест, чтобы позволить мне в режиме реального времени проверять пользовательский интерфейс RabbitMq, могу ли я видеть, как проходят и обрабатываются сообщения... поэтому я добавил больше логов.

Я быстро обнаружил, что сообщение было обработано слушателем, хотя мой тест не удался. Мой тест пытается проверить, что при сбое во время обработки сообщение отправляется в очередь рассылки сообщений. Итак, у меня есть слушатель в очереди на рассылку. Вот краткое изложение того, что я мог видеть в журналах:

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

Я сделал 2 вещи:

  1. убедиться, что очереди / обмены уникальны во время моей сборки

У меня было сомнение, что у меня может быть несколько потребителей в очереди из-за некоторых других заданий, работающих параллельно и соединяющихся в тех же обменах / очередях. Поэтому я хотел убедиться, что имена были уникальными для каждого запуска.

После Посмотрите имя хоста из Maven, это то, что я сделал:

  • Сконфигурируйте сборку Maven, чтобы получить доступ к имени хоста и внедрить его позже, через эту конфигурацию плагина Maven (ниже config должна быть заключена в элемент XML плагина, но по какой-то причине stackru не принимает его):

    <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
    

-> Я мог очень четко видеть, что ссылка на слушателя, от которого я утверждаю, и ссылка на слушателя, который получал сообщение, отличались! Очевидно, поэтому мой тест не проходит.

-> где-то в моем тестовом наборе еще один слушатель все еще был активен и принимал сообщение.

  1. Четко определить, какой потребитель получает сообщение

Чтобы сделать это, я четко зарегистрировал идентификатор слушателя во время создания:

@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?

Если у кого-то есть ответы на это, мне было бы интересно.

Другие вопросы по тегам