Автоматизация тестирования потребителей RabbitMQ
У меня есть микро-сервис .net, получающий сообщения с помощью клиента RabbitMQ, мне нужно проверить следующее:
1 - потребитель успешно подключен к хосту rabbitMq.
2 - потребитель слушает очередь.
3 - потребитель получает сообщения успешно.
для достижения вышеуказанного я создал пример приложения, которое отправляет сообщения, и я отлаживаю потребителя, чтобы быть уверенным, что он получает сообщения.
Как я могу автоматизировать этот тест? поэтому включите его в мой микро-сервис CI.
Я думаю включить мое пример приложения в мой CI, чтобы я мог запустить сообщение, а затем запустить модульный тест потребителя, который ждет определенное время, а затем проходит, если сообщение получено, но это кажется мне неправильной практикой, потому что тест не начнется, пока несколько секунд сообщение не будет уволен.
другой способ, о котором я думаю, - это запуск образца приложения из самого модульного теста, но если образец приложения не удается работа, которая сделает это ошибкой службы.
существует ли какая-либо передовая практика для интеграционного тестирования микроуслуг, подключающихся через RabbitMQ?
2 ответов
Я построил много таких тестов. Я бросил какой-то базовый код на Github здесь с .NET Core 2.0.
вам понадобится кластер RabbitMQ для этих автоматических тестов. Каждый тест начинается с исключения очереди, чтобы убедиться, что сообщения еще не существуют. Предварительно существующие сообщения из другого теста нарушат текущий тест.
у меня есть простой помощник для удаления очереди. В моих приложениях они всегда объявляют свои собственные очереди, но если это не ваш в этом случае вам придется снова создать очередь и любые привязки к любым обменам.
public class QueueDestroyer
{
public static void DeleteQueue(string queueName, string virtualHost)
{
var connectionFactory = new ConnectionFactory();
connectionFactory.HostName = "localhost";
connectionFactory.UserName = "guest";
connectionFactory.Password = "guest";
connectionFactory.VirtualHost = virtualHost;
var connection = connectionFactory.CreateConnection();
var channel = connection.CreateModel();
channel.QueueDelete(queueName);
connection.Close();
}
}
Я создал очень простой пример потребителя, который представляет микрослужб. Он работает в задаче до отмены.
public class Consumer
{
private IMessageProcessor _messageProcessor;
private Task _consumerTask;
public Consumer(IMessageProcessor messageProcessor)
{
_messageProcessor = messageProcessor;
}
public void Consume(CancellationToken token, string queueName)
{
_consumerTask = Task.Run(() =>
{
var factory = new ConnectionFactory() { HostName = "localhost" };
using (var connection = factory.CreateConnection())
{
using (var channel = connection.CreateModel())
{
channel.QueueDeclare(queue: queueName,
durable: false,
exclusive: false,
autoDelete: false,
arguments: null);
var consumer = new EventingBasicConsumer(channel);
consumer.Received += (model, ea) =>
{
var body = ea.Body;
var message = Encoding.UTF8.GetString(body);
_messageProcessor.ProcessMessage(message);
};
channel.BasicConsume(queue: queueName,
autoAck: false,
consumer: consumer);
while (!token.IsCancellationRequested)
Thread.Sleep(1000);
}
}
});
}
public void WaitForCompletion()
{
_consumerTask.Wait();
}
}
потребитель имеет интерфейс IMessageProcessor который сделает работу обрабатывать сообщение. В своем интеграционном тесте я создал подделку. Вы, вероятно, использовали бы для этого предпочтительную насмешливую структуру.
проверку издателя публикует сообщение в очередь.
public class TestPublisher
{
public void Publish(string queueName, string message)
{
var factory = new ConnectionFactory() { HostName = "localhost", UserName="guest", Password="guest" };
using (var connection = factory.CreateConnection())
using (var channel = connection.CreateModel())
{
var body = Encoding.UTF8.GetBytes(message);
channel.BasicPublish(exchange: "",
routingKey: queueName,
basicProperties: null,
body: body);
}
}
}
мой пример теста выглядит так:
[Fact]
public void If_SendMessageToQueue_ThenConsumerReceiv4es()
{
// ARRANGE
QueueDestroyer.DeleteQueue("queueX", "/");
var cts = new CancellationTokenSource();
var fake = new FakeProcessor();
var myMicroService = new Consumer(fake);
// ACT
myMicroService.Consume(cts.Token, "queueX");
var producer = new TestPublisher();
producer.Publish("queueX", "hello");
Thread.Sleep(1000); // make sure the consumer will have received the message
cts.Cancel();
// ASSERT
Assert.Equal(1, fake.Messages.Count);
Assert.Equal("hello", fake.Messages[0]);
}
моя подделка такова:
public class FakeProcessor : IMessageProcessor
{
public List<string> Messages { get; set; }
public FakeProcessor()
{
Messages = new List<string>();
}
public void ProcessMessage(string message)
{
Messages.Add(message);
}
}
Дополнительные советы:
если вы можете добавлять рандомизированный текст в очередь и обмениваться именами при каждом тестовом запуске, сделайте это, чтобы избежать параллельных тестов, мешающих друг другу
у меня есть некоторые помощники в коде для объявления очередей, обменов и привязок, если ваш приложения этого не делают.
напишите класс убийцы соединений, который заставит закрыть соединения и проверить, что ваши приложения все еще работают и могут восстановиться. У меня есть код для этого, но не в .NET Core. Просто попросите меня об этом, и я могу изменить его для работы в .NET Core.
В общем, я думаю, что вы должны избегать включения других микросервисов в интеграционные тесты. Если вы отправляете сообщение от одной службы к другой и ожидаете, что сообщение например, затем создайте поддельного потребителя, который может издеваться над ожидаемым поведением. Если вы получаете сообщения от других служб, создайте поддельные издатели в проекте тестирования интеграции.
Я успешно делал такой тест. Вам нужен тестовый экземпляр RabbitMQ, test exchange для отправки сообщений и тестовая очередь для подключения к получению сообщений.
не издевайтесь над всем!
но с тестовым потребителем, производителем и тестовым экземпляром rabbitMQ в этом тесте нет фактического производственного кода.
используйте тестовый экземпляр rabbitMQ и реальное применение
чтобы проверить meaniningfull я будет использовать тестовый экземпляр RabbitMQ, exchange и queue, но оставить реальное приложение (производитель и потребитель).
Я бы реализовал следующий сценарий
когда тестовое приложение делает что-то, что тестовое сообщение для rabbitMQ
тогда количество полученных сообщений в rabbitMQ увеличивается тогда
приложение делает то, что он должен делать при получении сообщения
шаги 1 и 3 приложения. Ваше приложение отправляет сообщения rabbitMQ на основе некоторого внешнего события (HTTP-сообщение получено? событие таймера?). Вы можете воспроизвести такое условие в своем тесте, поэтому приложение отправит сообщение (для тестирования экземпляра rabbitMQ).
та же история и для проверки действия приложения при получении сообщения. Приложение должно делать что-то наблюдаемое при получении сообщений. Если приложение делает HTTP-вызов-тогда вы можете издеваться над этой конечной точкой HTTP и проверять полученные сообщения. Если приложение сохраняет сообщения в базе данных - вы можете объединить базу данных для поиска Вашего сообщения.
использовать API мониторинга rabbitMQ
Шаг 2 может быть реализован с помощью API мониторинга RabbitMQ (есть методы, чтобы увидеть количество сообщений, полученных и потребленных из очереди https://www.rabbitmq.com/monitoring.html#rabbitmq-metrics)
рассмотрите возможность использования spring boot для проверки работоспособности
Если вы на основе java, а затем с помощью Spring Boot значительно упростит вашу проблему. Вы автоматически получите проверку здоровья для вашего соединения rabbitMQ!
см.https://spring.io/guides/gs/messaging-rabbitmq/ для учебника, как подключиться к RabbitMQ с помощью Spring boot. Приложение Spring boot предоставляет сведения о работоспособности (используя конечную точку HTTP /работоспособность) для каждого подключенного внешнего ресурса (базы данных, обмена сообщениями, ОМС и т. д.) Смотри https://docs.spring.io/spring-boot/docs/current/reference/html/production-ready-endpoints.html#_auto_configured_healthindicators для деталей.
Если соединение с rabbitMQ отключено, то проверка работоспособности (выполняется организацией.springframework.сапог.приводить в действие.протокол AMQP.RabbitHealthIndicator) вернет HTTP-код 4xx и meaninfull JSON-сообщение в теле JSON.
вам не нужно делать ничего особенного, чтобы иметь эту проверку здоровья-просто используя орг.springframework.boot: spring-boot-starter-amqp как зависимость maven/gradle достаточно.
тест CI-из каталога src / test
Я написал такой тест (который подключается к внешнему тестовому экземпляру RabbitMQ), используя интеграционные тесты, в каталоге src/test. При использовании Spring Boot проще всего сделать это с помощью тестового профиля и с подробностями подключения к тестовому экземпляру RabbitMQ в application-test.свойства (производство может использовать профиль производства, а применение-производство.файл свойств с производственным экземпляром RabbitMQ).
в простейшем случае (просто проверьте соединение с rabbitMQ) все, что вам нужно, это запустить приложение нормально и проверить /конечную точку работоспособности.
в этом случае я бы сделал следующие шаги CI
- тот, который строит (gradle build)
- один, что запускать модульные тесты (испытания без каких-либо внешних dependenices)
- тот, который запускает интеграцию тесты
тест CI-внешний
описанный выше подход также может быть выполнен для приложения, развернутого в тестовой среде (и подключенного к экземпляру test rabbitMQ). Как только приложение запускается, вы можете проверить /health endpoint, чтобы убедиться, что он подключен к экземпляру rabbitMQ.
Если вы заставите свое приложение отправить сообщение в rabbitMQ, то вы можете наблюдать метрики RabbitMQ (используя API мониторинга rabbitMQ) и наблюдать внешние эффекты использования сообщения приложением.
для такого теста вам нужно запустить и развернуть приложение из CI для запуска тестов.
для этого сценария я бы сделал следующие шаги CI
- шаг, который строит приложение
- шаги, которые запускают все тесты в src / test directory (блок, интеграция)
- шаг, который развертывает приложение в тестовой среде, или запускает dockerized application
- шаг запуска внешних тесты
- для dockerized среды, шаг, который останавливает контейнеры docker
рассмотрим dockerized enevironment
для внешнего теста вы можете запустить приложение Вместе с экземпляром test RabbitMQ в Docker. Вам понадобятся два контейнера docker.
- С приложением
- один с rabbitMQ . Существует официальный докер изображение для rabbitmq https://hub.docker.com/_/rabbitmq/ и это действительно простой в использовании
чтобы запустить эти два изображения, наиболее разумно написать файл docker-compose.