Интеграционные тесты 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 вещи:
- убедиться, что очереди / обмены уникальны во время моей сборки
У меня было сомнение, что у меня может быть несколько потребителей в очереди из-за некоторых других заданий, работающих параллельно и соединяющихся в тех же обменах / очередях. Поэтому я хотел убедиться, что имена были уникальными для каждого запуска.
После Посмотрите имя хоста из 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
-> Я мог очень четко видеть, что ссылка на слушателя, от которого я утверждаю, и ссылка на слушателя, который получал сообщение, отличались! Очевидно, поэтому мой тест не проходит.
-> где-то в моем тестовом наборе еще один слушатель все еще был активен и принимал сообщение.
- Четко определить, какой потребитель получает сообщение
Чтобы сделать это, я четко зарегистрировал идентификатор слушателя во время создания:
@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?
Если у кого-то есть ответы на это, мне было бы интересно.