我已经建立了许多这样的测试。我已经抛出了一些基本的代码
GitHub here with .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 中运行。
-
总的来说,我认为您应该避免在集成测试中包含其他微服务。例如,如果您将消息从一个服务发送到另一个服务并期望返回消息,则创建一个可以模拟预期行为的假消费者。如果您收到来自其他服务的消息,请在您的集成测试项目中创建虚假发布者。