Following is the method to receive the message on RabbitMq Queue. Method works fine but the unit testing this method has problem mentioned below
public void GetMessage(Action<string> action)
{
//create a connection factory
var factory = new ConnectionFactory();
if (_connectionString != null)
{
//assign connection string of rabbitmq
factory.Uri = new Uri(_connectionString);
}
//create connection and channel from connectionfactory
using (var connection = factory.CreateConnection())
using (var channel = connection.CreateModel())
{
//declare the queque on the channel
channel.QueueDeclare(queue: _queque,
durable: false,
exclusive: false,
autoDelete: false,
arguments: null);
//create consumer using the channel
var consumer = new EventingBasicConsumer(channel);
consumer.Received = (model, ea) =>
{
//When message is recieved on the consumer this will be triggered
var body = ea.Body.ToArray();
_message = Encoding.UTF8.GetString(body);
//call the method passed by the caller method
action(_message);
};
//add the consumer on this channel
channel.BasicConsume(queue: _queque,
autoAck: true,
consumer: consumer);
Console.ReadLine();
}
}
How to write a unit test test above message. Below unit test loading forever inside the call back method and the execution never goes to Assert.Equal line.
[Fact]
public async Task AddMessageAsync_ShouldAddToQueue()
{
//Arrange
string messageToSend = "Test";
string recievedMessage = null;
var queue = GetQueueManager();
queue.Message = messageToSend;
await queue.AddMessageAsync();
//Act
queue.GetMessage((string message) =>
{
//Hit here and recieved the message but does not exist from this method
recievedMessage = message;
});
//Assert
Assert.Equal(messageToSend, recievedMessage);
}
CodePudding user response:
One way to adress the issue would be a synchronization barrier. Here is an example: (There may be more efficient ways, though.)
... // Your code with some changes:
//create connection and channel from connectionfactory
using (var connection = factory.CreateConnection())
using (var channel = connection.CreateModel())
{
//declare the queque on the channel
channel.QueueDeclare(queue: _queque,
durable: false,
exclusive: false,
autoDelete: false,
arguments: null);
// Set up a synchronization barrier.
using (var barrier = new ManualResetEventSlim(false))
{
//create consumer using the channel
var consumer = new EventingBasicConsumer(channel);
consumer.Received = (model, ea) =>
{
//When message is recieved on the consumer this will be triggered
var body = ea.Body.ToArray();
_message = Encoding.UTF8.GetString(body);
try // you don't want "injected" code to break yours.
{
//call the method passed by the caller method
action(_message);
}
catch(Exception)
{
// at least log it!
}
finally
{
barrier.Set(); // Signal Event fired.
}
};
//add the consumer on this channel
channel.BasicConsume(queue: _queque,
autoAck: true,
consumer: consumer);
barrier.Wait(); // Wait for Event to fire.
}
}
I would imagine that the RabbitMQ API has had an update to support async/await instead of event-based? If so, then using that would of course be preferable.
If not: You may also want to explore Tasks and the Event-based Asynchronous Pattern (EAP)