Thursday 28 September 2017

Unit Testing using Moq

What is Moq ?

Moq is a third party library/framework that enables you to create the dummy class and its methods’ dummy implementation without actually creating a class with actual method implementation. Here you need to note that you need to provide the interface name for creating dummy class which will implement interface methods as dummy. This is very useful in the scenarios where you want to test the functionality of a class without implementing the other classes and their methods on which the test candidate class is dependent on.

How to install “Moq” in your test project ?

You can install “Moq’ in your test project using “NuGet Package Manager” UI or console. You can find “Manager NuGet Package” by right clicking on your project solution. When you click on it you will get a UI where you can search “Moq” online and install it to your test project. Now you are ready to go with using “Moq”.

How to use “Moq” in your project to test library classes?

Lets say that you have a class “DefaultMailer” which implements interface “IMailer” with method “SendMail”. SendMail needs an object, of type “IMailClient” as parameter, which will be used to actually send the mail.
Here, IMailer has only four properties, for simplicity, which are necessary to send mail.
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace YourNamespace.Utility.Interfaces
{
    public interface IMailer
    {
        string From { get; set; }
        string To { get; set; }
        string Subject { get; set; }
        string Body { get; set; }

        bool SendMail(IMailClient mailClient);
    }
}
IMailClient has only one methods which is used to send mail. It has only two properties i.e. Host and port. Here you can have many MailClients for supported mail servers like gmail, yahoo, hotmail etc.
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace YourNamespace.Utility.Interfaces
{
    public interface IMailClient
    {
        string Server { get; set; }
        string Port { get; set; }

        bool SendMail(string from, string to, string subject, string body);
    }
}
DefaultMailer implements IMailer interface.
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using YourNamespace.Utility.Interfaces;

namespace YourNamespace.Utility
{
    public class DefaultMailer : IMailer 
    {
        public string From { get; set; }
        public string To { get; set; }
        public string Subject { get; set; }
        public string Body { get; set; }

        public bool SendMail(IMailClient mailClient)
        {
            return mailClient.SendMail(this.From, this.To, this.Subject, this.Body);
        }
    }
}
Now we will see how to test if your DefaultMailer > SendMail method is working as expected. To do this, create a UnitTest project in your solution and install Moq using NuGet package manager. Create a UnitTest class as below.
using System;
using Microsoft.VisualStudio.TestTools.UnitTesting;
using YourNamespace.Utility.Interfaces;
using YourNamespace.Utility;
using Moq;
using Moq.Matchers;

namespace YourNamespace.Utility.TestFixtures
{
    [TestClass]
    public class MailerTestFixtures
    {
        [TestMethod]
        public void SendMailTestFixture()
        {
            //Create a mock object of a MailClient class which implements IMailClient
            var mockMailClient = new Moq.Mock<IMailClient>();

            //Mock the properties of MailClient
            mockMailClient.SetupProperty(client => client.Server, "chat.mail.com").SetupProperty(client => client.Port, "1212");

            //Configure dummy method so that it return true when it gets any string as parameters to the method
            mockMailClient.Setup(client => client.SendMail(It.IsAny<string>(),It.IsAny<string>(),It.IsAny<string>(),It.IsAny<string>())).Returns(true);

            IMailer mailer = new DefaultMailer() { From = "from@mail.com", To="to@mail.com", Subject="Using Moq", Body="Moq is awesome"};

            //Use the mock object of MailClient instead of actual object
            var result = mailer.SendMail(mockMailClient.Object);

            //Verify that it return true
            Assert.IsTrue(result);

            //Verify that the MailClient's SendMail methods gets called exactly once when string is passed as parameters
            mockMailClient.Verify(client => client.SendMail(It.IsAny<string>(), It.IsAny<string>(), It.IsAny<string>(), It.IsAny<string>()), Times.Once);
        }
    }
}
Now I will explain the syntax of Moq I have used in the above test case. This is the most simple test case you can use. I have kept it simple here so that you can at least get started with its basics without worrying about the other complex syntax of Moq.

Creating the object of dummy class which implements an interface.

//Create a mock object of a MailClient class which implements IMailClient
var mockMailClient = new Moq.Mock<IMailClient>();
In this example, I have created dummy class which implements the interface IMailClient. The result would be a factory which will give me object of this dummy class by using its property Object.

Setup the properties of dummy class as per your need.

/Mock the properties of MailClient
mockMailClient.SetupProperty(client => client.Server, "chat.mail.com")
              .SetupProperty(client => client.Port, "1212");
You can set the default value for the properties of your dummy class objects using the “SetupProperty” provided by Moq.

Setup the methods of dummy class as per your need.

//Configure dummy method so that it return true when it gets any string as parameters to the method
mockMailClient.Setup(client => client.SendMail(It.IsAny<string>(),It.IsAny<string>(),It.IsAny<string>(),It.IsAny<string>())).Returns(true);
Here we are setting up the SendMail method of your dummy class.  The meaning of the statements is that “When someone invokes the method “SendMail” of dummy class object with four parameters, which may be any string value, then return true. You can modify the statement to configure the cases where user passes null or empty or particular string.

Use the mock/dummy object to test your class “DefaultMailer”

//Use the mock object of MailClient instead of actual object
var result = mailer.SendMail(mockMailClient.Object);

//Verify that it return true
Assert.IsTrue(result);
Here we are using the object of mock/dummy class instead of actual IMailClient implementation. The reason behind this here is that we need to test our class “DefaultMailer” without depending on any other classes. We should assume here that other classes are working fine as we should have written separate test cases to test them.

Verify that the particular method of the interface was invoked

//Verify that the MailClient's SendMail methods gets called exactly once when string is passed as parameters
mockMailClient.Verify(client => client.SendMail(It.IsAny<string>(), It.IsAny<string>(), It.IsAny<string>(), It.IsAny<string>()), Times.Once);
Here we verify that the method of the client was actually invoked by DefaultMailer to send mail. We also ensure that the method was invoked just once. In case, it was not invoked or invoked more than once then it will fail the test case.
You can learn more about Moq here.



So if our method is using the database access layer class, then instead of directly creating an object of this class, we can pass the dependency using an interface to the method. The advantage of this approach is that we can easily pass another database access class, or even our mock class, instead of always passing the same class.
mocking framework

Note: Here I have not concentrated on the design for actual implementation of MailClient or MailSender. I have kept it simple so that you can understand Moq without worrying about implementation of actuall MailClient or MailSender.
I hope it will be useful for you to start using Moq to test your methods without creating actual objects of dependency classes. You will learn about the syntax of Moq and get familiar with it as much as you practice. At first you will find it difficult because of its Action, Func, LINQ implementations but once you start using it, you will find it fun and proficient to use Moq.

No comments:

Post a Comment