Автоматизация тестирования потребителей 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, но оставить реальное приложение (производитель и потребитель).

Я бы реализовал следующий сценарий

  1. когда тестовое приложение делает что-то, что тестовое сообщение для rabbitMQ

  2. тогда количество полученных сообщений в rabbitMQ увеличивается тогда

  3. приложение делает то, что он должен делать при получении сообщения

шаги 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.