Автоматизировать потребительское тестирование RabbitMQ - PullRequest
0 голосов
/ 04 мая 2018

У меня есть микросервис .net, получающий сообщения с помощью клиента RabbitMQ, мне нужно проверить следующее:

1 - потребитель успешно подключен к хосту rabbitMq.

2 - потребитель слушает очередь.

3 - потребитель успешно получает сообщения.

Для достижения вышеизложенного я создал образец приложения, которое отправляет сообщения, и я отлаживаю потребителя, чтобы убедиться, что он получает сообщения.

Как я могу автоматизировать этот тест? следовательно, включите его в мой микросервис CI.

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

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

Существуют ли передовые практики для интеграционного тестирования микросервисов, подключающихся через RabbitMQ?

Ответы [ 2 ]

0 голосов
/ 11 мая 2018

Я построил много таких тестов. Я бросил некоторый основной код на 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.

  • В общем, я думаю, вам следует избегать включения других микросервисов в ваши интеграционные тесты. Если вы отправляете сообщение от одной службы к другой и ожидаете, например, сообщение обратно, то создайте поддельного потребителя, который может высмеивать ожидаемое поведение. Если вы получаете сообщения от других служб, создайте поддельных издателей в своем проекте интеграционного тестирования.

0 голосов
/ 07 мая 2018

Я успешно прошел такой тест. Вам нужен тестовый экземпляр RabbitMQ, тестовый обмен для отправки сообщений и тестовая очередь для подключения к получению сообщений.

Не надо издеваться над всем!

Но для тестового потребителя, производителя и тестового экземпляра rabbitMQ в этом тесте нет действительного производственного кода.

использовать тестовый экземпляр rabbitMQ и реальное приложение

Для получения полного теста я бы использовал тестовый экземпляр RabbitMQ, обмен и очередь, но оставлял реальное приложение (производитель и потребитель).

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

  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)

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

Если вы используете Java, а затем использование Spring Boot значительно упростит вашу проблему. Вы автоматически получите проверку здоровья для вашего соединения rabbitMQ!

См. https://spring.io/guides/gs/messaging-rabbitmq/, чтобы узнать, как подключиться к RabbitMQ с помощью Spring boot. Загрузочное приложение Spring предоставляет информацию о работоспособности (используя конечную точку HTTP / работоспособность) для каждого подключенного внешнего ресурса (базы данных, сообщений, jms и т. Д.) Подробнее см. https://docs.spring.io/spring-boot/docs/current/reference/html/production-ready-endpoints.html#_auto_configured_healthindicators.

Если соединение с rabbitMQ не работает, проверка работоспособности (выполняется org.springframework.boot.actuate.amqp.RabbitHealthIndicator) возвращает HTTP-код 4xx и среднее json-сообщение в теле JSON.

Вам не нужно ничего делать для проверки работоспособности - просто используйте org.springframework.boot: spring-boot-starter-amqp, так как зависимости maven / gradle достаточно.

CI test- из каталога src / test

Я написал такой тест (который подключается к внешнему тестовому экземпляру RabbitMQ) с использованием интеграционных тестов в каталоге src / test. При использовании Spring Boot это проще всего сделать с помощью тестового профиля и подробных данных о подключении для тестирования экземпляра RabbitMQ в application-test.properties (production может использовать производственный профиль и файл application-production.properties с производственным экземпляром RabbitMQ).

В простейшем случае (просто проверьте соединение с rabbitMQ) все, что вам нужно, это нормально запустить приложение и проверить конечную точку / работоспособность.

В этом случае я бы сделал следующие шаги CI

  • тот, который строит (gradle build)
  • тот, который запускает модульные тесты (тесты без каких-либо внешних зависимостей)
  • тот, который запускает интеграционные тесты

CI тест - внешний

Вышеописанный подход также может быть реализован для приложения, развернутого в тестовой среде (и подключенного к тестовому экземпляру rabbitMQ). Как только приложение запускается, вы можете проверить / Health Endpoint, чтобы убедиться, что оно подключено к экземпляру rabbitMQ.

Если вы заставляете ваше приложение отправлять сообщения в rabbitMQ, то вы можете наблюдать метрики rabbbitMQ (используя API мониторинга rabbitMQ) и наблюдать внешние эффекты использования сообщения приложением.

Для такого теста вам нужно запустить и развернуть приложение из CI перед началом тестирования.

для этого сценария я бы сделал следующие шаги CI

  • шаг, который строит приложение
  • шагов, которые запускают все тесты в каталоге src / test (модуль, интеграция)
  • шаг, который развертывает приложение в тестовой среде или запускает докеризованное приложение
  • шаг, который запускает внешние тесты
  • для среды Docker, шаг, который останавливает Docker-контейнеры

Рассмотрим докеризированную среду

Для внешнего теста вы можете запустить ваше приложение вместе с тестовым экземпляром RabbitMQ в Docker. Вам понадобятся два док-контейнера.

  • один с приложением
  • один с кроликом MQ. Существует официальный образ докера для rabbitmq https://hub.docker.com/_/rabbitmq/, и он действительно прост в использовании

Для запуска этих двух образов наиболее разумно написать файл docker-compose.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...