web-dev-qa-db-ja.com

RabbitMQ消費者テストを自動化する

RabbitMQクライアントを使用してメッセージを受信する.netマイクロサービスがあります。次をテストする必要があります。

1-コンシューマはrabbitMqホストに正常に接続されました。

2-消費者はキューをリッスンしています。

3-消費者はメッセージを正常に受信しています。

上記を達成するために、メッセージを送信するサンプルアプリケーションを作成し、メッセージを受信して​​いることを確認するためにコンシューマをデバッグしています。

このテストを自動化するにはどうすればよいですか?したがって、それをマイクロサービスCIに含めます。

サンプルアプリをCIに含めて、メッセージを起動し、特定の時間待機してからメッセージが受信されたら合格するコンシューマユニットテストを実行できるように考えていますが、テストは開始されないため、これは間違った練習のようです数秒まで、メッセージが起動されます。

私が考えているもう1つの方法は、単体テスト自体からサンプルアプリケーションを起動することですが、サンプルアプリが機能しない場合、サービスエラーになります。

RabbitMQを介して接続するマイクロサービスの統合テストのベストプラクティスはありますか?

11
Yahya Hussein

私はそのようなテストを数多く作成しました。ここで.NET Core 2.0を使用した Githubにいくつかの基本的なコードをスローしました。

これらの自動テストには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で実行できます。

  • 一般に、統合テストに他のマイクロサービスを含めることは避けるべきだと思います。あるサービスから別のサービスにメッセージを送信し、たとえばメッセージが返されることを期待する場合、予想される動作を模倣できる偽のコンシューマーを作成します。他のサービスからメッセージを受信する場合、統合テストプロジェクトで偽のパブリッシャーを作成します。

8
Vanlightly

私はこの種のテストに成功しました。 RabbitMQのテストインスタンス、メッセージを送信するためのテスト交換、およびメッセージを受信するために接続するためのテストキューが必要です。

すべてをモックしないでください!

ただし、rabbitMQのテストコンシューマ、プロデューサ、およびテストインスタンスでは、そのテストには実際の製品コードはありません。

テストrabbitMQインスタンスと実際のアプリケーションを使用する

意味のある完全なテストを行うには、テストRabbitMQインスタンス、交換、キューを使用しますが、実際のアプリケーション(プロデューサーとコンシューマー)はそのままにします。

私は次のシナリオを実装します

  1. テストアプリケーションがrabbitMQへのメッセージをテストする何かをするとき

  2. その後、rabbitMQで受信したメッセージの数が増加します

  3. アプリケーションは、メッセージの受信時に行うべきことを行います

ステップ1と3はアプリケーション固有です。アプリケーションは、外部イベント(HTTPメッセージの受信?タイマーイベント?)に基づいて、rabbitMQにメッセージを送信します。テストでそのような状態を再現できるため、アプリケーションは(rabbitMQインスタンスをテストするために)メッセージを送信します。

メッセージ受信時のアプリケーションアクションの検証についても同じです。アプリケーションは、メッセージを受信したときに観察可能なことを行う必要があります。アプリケーションがHTTP呼び出しを行う場合、そのHTTPエンドポイントをモックし、受信したメッセージを確認できます。アプリケーションがデータベースにメッセージを保存する場合、データベースをプールしてメッセージを検索できます。

rabbitMQモニタリングAPIを使用

ステップ2は、RabbitMQモニタリングAPIを使用して実装できます(キューから受信および消費されるメッセージの数を確認するメソッドがあります https://www.rabbitmq.com/monitoring.html#rabbitmq-metrics

スプリングブートを使用してヘルスチェックを行うことを検討する

Javaベースで、Spring Bootを使用すると、問題が大幅に簡素化されます。 rabbitMQ接続のヘルスチェックを自動的に取得します!

Springブートを使用してRabbitMQに接続する方法のチュートリアルについては、 https://spring.io/guides/gs/messaging-rabbitmq/ を参照してください。 Springブートアプリケーションは、接続されているすべての外部リソース(データベース、メッセージング、jmsなど)の正常性情報(HTTPエンドポイント/ healthを使用)を公開します- https://docs.spring.io/spring-boot/docs/current/ reference/html/production-ready-endpoints.html#_auto_configured_healthindicators をご覧ください。

RabbitMQへの接続がダウンしている場合、ヘルスチェック(org.springframework.boot.actuate.amqp.RabbitHealthIndicatorによって行われます)は、JSON本文でHTTPコード4xxおよびmeaninfull jsonメッセージを返します。

Maven/gradleの依存関係で十分であるため、org.springframework.boot:spring-boot-starter-amqpを使用するだけで、ヘルスチェックを行うために特別なことを行う必要はありません。

CIテスト-src/testディレクトリから

Src/testディレクトリで、統合テストを使用してこのようなテスト(RabbitMQの外部テストインスタンスに接続する)を作成しました。 Spring Bootを使用する場合は、テストプロファイルを使用し、application-test.propertiesにテストRabbitMQインスタンスへの接続の詳細を含めるのが最も簡単です(本番環境ではプロダクションプロファイルを使用でき、application-production.propertiesファイルには本番用RabbitMQインスタンスを使用できます)。

最も単純な場合(rabbitMQへの接続を確認するだけ)に必要なのは、アプリケーションを正常に起動し、/ healthエンドポイントを検証することだけです。

この場合、次のCIステップを実行します

  • ビルドするもの(gradle build)
  • 単体テスト(外部依存関係のないテスト)を実行するもの
  • 統合テストを実行するもの

CIテスト-外部

上記のアプローチは、テスト環境にデプロイされた(およびテストrabbitMQインスタンスに接続された)アプリケーションに対しても実行できます。アプリケーションが起動したらすぐに、/ healthエンドポイントをチェックして、rabbitMQインスタンスに接続されていることを確認できます。

アプリケーションがrabbitMQにメッセージを送信するようにすると、rabbMQメトリックを(rabbitMQモニタリングAPIを使用して)観察し、アプリケーションによって消費されるメッセージの外部効果を観察できます。

そのようなテストでは、テストを開始する前に、CIからアプリケーションを開始してデプロイする必要があります。

そのシナリオでは、次のCIステップを実行します

  • アプリをビルドするステップ
  • src/testディレクトリ内のすべてのテストを実行する手順(ユニット、統合)
  • テスト環境にアプリをデプロイするステップ、またはドッキングされたアプリケーションを開始するステップ
  • 外部テストを実行するステップ
  • docker環境の場合、Dockerコンテナを停止するステップ

ドッキングされた環境を考慮する

外部テストでは、DockerでテストRabbitMQインスタンスとともにアプリケーションを実行できます。 2つのdockerコンテナが必要です。

  • アプリケーション付きのもの
  • rabbitMQを使用したもの。 rabbitmqの公式dockerイメージがあります https://hub.docker.com/_/rabbitmq/ そしてそれは本当に使いやすいです

これらの2つのイメージを実行するには、docker-composeファイルを作成するのが最も合理的です。

5
Bartosz Bilicki