简体   繁体   中英

Unit Test using MOQ Nested Class & Interface C#

Hi I am having the below interfaces and their implementation. Now, I wanted to unit test the Send() method which will actually push the message into a queue. Since I am new to MoQ, not sure how to get it done.

public interface IAdapter 
{
    IChannel UseQueue(QueueDetail queueDetail);
}
public interface IChannel
{
    void Send(string key, byte[] message);
}

public class AdapternServiceBus : IAdapter
{   
    readonly IConnection connection;
    readonly IModel channel;

    public AdapternServiceBus(IConnection connection, IModel channel)
    {
        this.connection = connection;     
        this.channel = channel;
    }

    public IChannel BindAndUseQueue(QueueDetail queueDetail)
    {
        // Logic of creating and binding queue
        return new ServiceBusChannel(this, queueDetail.QueueName);
    }

    public IModel GetChannel()
    {
        return channel;
    }
}

public class ServiceBusChannel : IChannel
{
    readonly string containerName;
    IModel channel;

    public ServiceBusChannel(AdapternServiceBus adapter, string containerName)
    {   
        this.containerName = containerName;
        channel = adapter.GetChannel();
    }

    public void Send(string key, byte[] message)
    {
        // Publish the message
        channel.BasicPublish(exchangeName, key, null, message);
    }
}

Here through factory, we decide which type of framework we need to connect and within the factory, we open a connection which passed on to the next class, so that it can be consumed to create and bind queues. Implementation of IChannel is the main class which will be used to actually talk with the queue and topics.

You want to test the Send method in ServiceBusChannel class.

So, in your test, you should instantiate this actual concrete class.

To instantiate a ServiceBusChannel , you need an AdapterServiceBus that allows you to set up your channel variable.

Here, the problem is that you depend on a concrete AdapterServiceBus class, so you cannot mock it. You should depend on an interface that exposes the actual behavior you want to mock.

Something like IAdpaterServiceBus that declares GetChannel(); , (EDIT) or declare the GetChannel() as a method of your already existing IAdapter interace) .

Then you can Mock this :

var mockedAdapter = new Mock<IAdapterServiceBus>();

or

var mockedAdapter = new Mock<IAdapter>(); // if you added GetChannel() to IAdapter

then mock the behavior of GetChannel() :

mockedAdapter.Setup(x => x.GetChannel())
     .Returns( /* some mocked string value for your channel */);

Then you can pass this to your ServiceBusChannel constructor (modified so it accepts an abstract IAdapterServiceBus ( or IAdapter ) instead of a concrete instance)

var service = new ServiceBusChannel(mockedAdapter.Object, containerName);

The following minimal example shows how to unit test the ServiceBusChannel.Send method by providing the necessary dependencies to allow the test to be exercised to completion. It is based on the example provided by the original question.

[TestClass]
public class ServiceBusChannel_Should {
    [TestMethod]
    public void SendMessage() {
        //Arrange
        var channel = Mock.Of<IModel>();
        var connection = Mock.Of<IConnection>();
        var adapter = new AdapternServiceBus(connection, channel);
        var key = "somekey";
        var message = Encoding.UTF8.GetBytes("Hello World");
        var subject = new ServiceBusChannel(adapter, "containerName");

        //Act
        subject.Send(key, message);

        //Assert
        Mock.Get(channel).Verify(_ => _.BasicPublish(It.IsAny<string>(), 
                                         It.IsAny<string>(), 
                                         It.IsAny<bool>(), 
                                         It.IsAny<IBasicProperties>(), 
                                         It.IsAny<byte[]>())
                                  , Times.AtLeastOnce());
    }
}

Note: Moq framework was used to mock the abstract dependencies. Their Quickstart provided examples of how to use the framework.

While I believe that the subject under test should not be coupled to concretions, the AdapternServiceBus can be used as the entry point for the mocked dependencies as demonstrated above. You can modify to suit you specific needs as I assume the provided code is not the actual code used but an example.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM