RabbitMQ is a very good open source message queue server, but unfortunately it's DotNet API isn't very "dotnetish". Well, finally I figured out a way to unit test classes which relied upon IModel.
[TestFixture]public class RabbitMQTest{ [Test] public void CanMockMQ() { // Arrange var channelMock = new Mock<IModel>(); channelMock .Setup(m => m.BasicConsume(It.IsAny<string>(), It.IsAny<bool>(), It.IsAny<IDictionary>(), It.IsAny<IBasicConsumer>())) .Callback<string, bool, object, IBasicConsumer>((queue, noAck, props, consumer) => consumer.HandleBasicDeliver("", 1, false, "exchange", "route", null, new byte[0])); var server = new MQUser(channelMock.Object);
// Act server.Abort(); server.DoStuff();
// Assert channelMock.Verify(m => m.BasicAck(1, false)); }}public class MQUser{ readonly IModel channel;
public MQUser(IModel channel) { this.channel = channel; }
public void DoStuff() { var queueName = channel.QueueDeclare(); var subscription = new Subscription(channel, queueName); do { var msg = subscription.Next(); // Do stuff with the message subscription.Ack(msg); } while (!aborted); }
bool aborted; public void Abort() { aborted = true; }}
It uses Moq which let us mock the IModel interface and intercepts all calls to IModel#BasicConsume, which all subscribers must call to subscribe for messages, and then imeditly call IModel#HandleBasicDeliver which propagetes that a messages has arrived to the subscriber. Then the test continues by starting the class under test which contains a simple subscriber in a abortable loop. We will abort it before starting the loop (but becasue it's a do-while loop, it will try to fetch one message before returning). At last we verify that the message has been acknowledged.