RabbitMQクライアントを使用してメッセージを受信する.netマイクロサービスがあります。次をテストする必要があります。
1-コンシューマはrabbitMqホストに正常に接続されました。
2-消費者はキューをリッスンしています。
3-消費者はメッセージを正常に受信しています。
上記を達成するために、メッセージを送信するサンプルアプリケーションを作成し、メッセージを受信していることを確認するためにコンシューマをデバッグしています。
このテストを自動化するにはどうすればよいですか?したがって、それをマイクロサービスCIに含めます。
サンプルアプリをCIに含めて、メッセージを起動し、特定の時間待機してからメッセージが受信されたら合格するコンシューマユニットテストを実行できるように考えていますが、テストは開始されないため、これは間違った練習のようです数秒まで、メッセージが起動されます。
私が考えているもう1つの方法は、単体テスト自体からサンプルアプリケーションを起動することですが、サンプルアプリが機能しない場合、サービスエラーになります。
RabbitMQを介して接続するマイクロサービスの統合テストのベストプラクティスはありますか?
私はそのようなテストを数多く作成しました。ここで.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で実行できます。
一般に、統合テストに他のマイクロサービスを含めることは避けるべきだと思います。あるサービスから別のサービスにメッセージを送信し、たとえばメッセージが返されることを期待する場合、予想される動作を模倣できる偽のコンシューマーを作成します。他のサービスからメッセージを受信する場合、統合テストプロジェクトで偽のパブリッシャーを作成します。
私はこの種のテストに成功しました。 RabbitMQのテストインスタンス、メッセージを送信するためのテスト交換、およびメッセージを受信するために接続するためのテストキューが必要です。
すべてをモックしないでください!
ただし、rabbitMQのテストコンシューマ、プロデューサ、およびテストインスタンスでは、そのテストには実際の製品コードはありません。
テストrabbitMQインスタンスと実際のアプリケーションを使用する
意味のある完全なテストを行うには、テストRabbitMQインスタンス、交換、キューを使用しますが、実際のアプリケーション(プロデューサーとコンシューマー)はそのままにします。
私は次のシナリオを実装します
テストアプリケーションがrabbitMQへのメッセージをテストする何かをするとき
その後、rabbitMQで受信したメッセージの数が増加します
アプリケーションは、メッセージの受信時に行うべきことを行います
ステップ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ステップを実行します
CIテスト-外部
上記のアプローチは、テスト環境にデプロイされた(およびテストrabbitMQインスタンスに接続された)アプリケーションに対しても実行できます。アプリケーションが起動したらすぐに、/ healthエンドポイントをチェックして、rabbitMQインスタンスに接続されていることを確認できます。
アプリケーションがrabbitMQにメッセージを送信するようにすると、rabbMQメトリックを(rabbitMQモニタリングAPIを使用して)観察し、アプリケーションによって消費されるメッセージの外部効果を観察できます。
そのようなテストでは、テストを開始する前に、CIからアプリケーションを開始してデプロイする必要があります。
そのシナリオでは、次のCIステップを実行します
ドッキングされた環境を考慮する
外部テストでは、DockerでテストRabbitMQインスタンスとともにアプリケーションを実行できます。 2つのdockerコンテナが必要です。
これらの2つのイメージを実行するには、docker-composeファイルを作成するのが最も合理的です。