Introduction
Unit tests have become integral part of any build now days. Unit tests should be written normally for smallest units of code i.e. methods doing some specific and independent work. But, what if your method is calling different layers (like Method => BusinessLogicLayer =>DataLayer) to get its work done and you want to write unit tests for that? If your unit test also calls all these layers in actual it doesn’t go with the actual purpose of unit tests. Unit test should definitely not call other layers for sure. Situation becomes a bit tricky and difficult when you want to write a unit test for a method which calls a WCF service internally.
To overcome from this problem we use Mock Frameworks. There are many Mock frameworks available in the market but I will be discussing mocking using MOQ here. Let us take wcf scenario and try to mock a wcf method using MOQ mock framework while writing a unit test for wcf client method (which calls wcf service).
I will highlight some other features also later in this article.
Using the Code
My wcf service WCFService
is derived from an interface IWCFService
and has a method GetData(int value)
namespace WCFService
{
public class WCFService : IWCFService
{
public string GetData(int value)
{
return string.Format("You entered: {0}", value);
}
}
}
A wcf client WCFClient
consumes WCFService
using a service agent WCFServiceAgent.
Service agent implements a method HitWCFService
()
which calls service method GetData()
namespace WCFClient
{
public class WCFServiceAgent
{
IWCFService wcfService;
public WCFServiceAgent()
{
this.wcfService = new WCFServiceClient();
}
public WCFServiceAgent(bool isUnitTest)
{
this.wcfService = UnityHelper.IoC.Resolve<IWCFService>();
}
public string HitWCFService()
{
int val = 1; string retVal = string.Empty;
retVal = this.wcfService.GetData(val);
return retVal;
}
}
}
Now, we want to write a unit test for HitWCFService()
. To avoide actual service call we need to mock wcfService.GetData(val)
method.
namespace WCFClientTest
{
[TestClass]
public class WCFClientUnitTest
{
[TestMethod]
public void MockWCFService()
{
string val = "MockedValue";string actualRetVal;
Mock<IWCFService>wcfMock = new Mock<IWCFService>();
wcfMock.Setup<string>(s=> s.GetData(It.IsAny<int>())).Returns(val);
IWCFService wcfMockObject = wcfMock.Object;
UnityHelper.IoC = new UnityContainer();
UnityHelper.IoC.RegisterInstance<IWCFService>(wcfMockObject);
WCFServiceAgent serviceAgent = new WCFServiceAgent(true);
actualRetVal = serviceAgent.HitWCFService();
wcfMock.Verify(s => s.GetData(It.IsAny<int>()), Times.Exactly(1));
Assert.AreEqual("MockedValue", actualRetVal, "Not same.");
}
}
}
In above written example, we first create a mock object of IWCFService
interface. Mock<IWCFService> wcfMock = new Mock<IWCFService>();
Just to remember, mock object cannot be created for a concrete class i.e. it is not possible to write something like
Mock<WCFService> wcfMock = new Mock<WCFService>().wcfMock.Setup<string>(s => s.GetData(It.IsAny<int>())).Returns(val);
using above specified line of code we tell mock framework that we wish to mock GetData method and the return value will be val
. In other words, when GetData()
is invoked imitate that this method is called and returned val
.
Now, we register the instance of wcfMockObject
with IoC which bounds IoC to return the same object when statement IoC.Resolve()
is encountered.
UnityHelper.IoC = new UnityContainer();
UnityHelper.IoC.RegisterInstance<IWCFService>(wcfMockObject);
I handled the situation with a small trick further. I used parameterized constructor to create the service client object in service agent. If you see the code, the simple constructor is used to create the ServiceAgent object (when actual application creates the object) but for unit test I used the parameterized constructor where IoC.Resolve gives me the registered mock object.
public WCFServiceAgent(bool isUnitTest)
{
this.wcfService = UnityHelper.IoC.Resolve<IWCFService>();
}
For your reference
UnityHelper
is nothing but as follows:
namespace WCFClient
{
public static class UnityHelper
{
public static UnityContainer IoC;
}
}
Now, second last line of my unit test method verifies if the expected method call happened or not.
wcfMock.Verify(s => s.GetData(It.IsAny<int>()),Times.Exactly(1));
This line can be written as
wcfMock.Verify(s => s.GetData(4), Times.Exactly(1));
but then your setup method should also be
wcfMock.Setup<string>(s => s.GetData(4)).Returns(val);
However, this will create problem if the parameter being passed to the mocked method is created or initialized inside the method we are writing the unit test for. I deliberately created the parameter val (val = 1) inside HitWCFService
. The mock framework considers both the methods different (GetData(4) and GetData(1) are not same) and when you verify it, it throws an error saying something like; “expected once but never called”.
If you don’t want MOQ to compare parameters use It.IsAny<int>(). This causes MOQ to ignore parameters while expecting mocked method.
For more, please see demo application.
- Amit Shrivastava, System Analyst, Avanade/Accenture (Gurgaon, India)