With Visual studio 2012 Ultimate edition Microsoft has shipped new Faking framework which enable developer to isolate their unit tests from their environment. Now it’s time to understand them.
When we say external dependencies it may be of two types1) Dependency to Interfaces or Abstract classes
Example
Customer Data Access Layer
public class CustomerDataAccessLayer
{
publicbool Insert()
{
try
{
//Insert into database
return true;
}
catch (Exception e)
{
return false;
}
}
}
Email library
public interface IEmailLibrary
{
bool SendEmail();
}
public class EmailLibrary : IEmailLibrary
{
public bool SendEmail()
{
//Send email code - > But right now email server is configure
return true;
}
}
Customer Middle Layer
public class CustomerMiddleLayer
{
public string AddCustomer(string CustomerName,IEmailLibrary emailLibrary)
{
CustomerDataAccessLayer ObjLayer = new CustomerDataAccessLayer();
ObjLayer.Insert();
emailLibrary.SendEmail();
return CustomerName;
}
}
Unit Testing
If we follow simple Unit Testing steps for testing AddCustomer method in CustomerMiddleLayer class it won’t work out. Test will always fail. We even won’t be able to test the other logics in the AddCustomer method.
One way to achieve this is
- Create your own temporary class , inherit it from IEmailLibrary and use
- Use Mock frameworks like MOQ, Rhino MOCK etc.
But I neither believe in
- Reinventing the wheel
- Nor asking other.
I mean, we can achieve same behavior with the help of Visual studio 2012
fakes.
fakes.
So let’s do it.
Step 1
In the Test Project expand reference tab, right click your assembly (MiddleLayer) and say generate Fake assembly.
Step 2
Import {YourNames}.Fakes in the top. In this case it will be
using MiddleLayer.Fakes;
Step 3
Create a new Test Method and use Stub version of IEmailLibrary instead of original Email library class.
[TestMethod]
public void TestAddMethodWithStubs()
{
//Arrange
CustomerMiddleLayer middleLayer = new CustomerMiddleLayer();
string CustomerName = "Sukesh Marla";
IEmailLibrary e = new StubIEmailLibrary
{
SendEmail=()=>true
};
//Act
string returnCustomerName = middleLayer.AddCustomer(CustomerName, e);
//Assert
Assert.AreEqual<string>(CustomerName, returnCustomerName);
}
If you try to run this unit test it will always pass unless and until there are some error in some other partof Add Customer Function. It’s behaving so because instead of original EmailLibrary class we are passing stub version of it. In the stub version we have implemented SendEmail function which won’t do anything and return true directly.
Now let’s define stubs in technical words.
Stubs are simply Concrete Implementation of interfaces and abstract classes which you can pass across. Using lambda expression we provide method implementations to it.
2) Dependency to concrete classes
Stubs are really easy to use and understand but in real life projects we will not find DIC (Dependency inversion principle)followed everywhere. In real life we cannot go and create interface every time we end up with dependency. Many time we will stick to concrete classes instead of interfaces and abstract classes. Let’s rewrite the above code but this time without interfaces.
Customer Data Access Layer
(Same as above)
Email library
public class EmailLibrary
{
public bool SendEmail()
{
//Send email code - > But right now email server is configure
return true;
}
}
Customer Middle Layer
public class CustomerMiddleLayer
{
public string AddCustomer(string CustomerName)
{
CustomerDataAccessLayer ObjLayer = new CustomerDataAccessLayer();
ObjLayer.Insert();
EmailLibrary emailLibrary= new EmailLibrary();
emailLibrary.SendEmail();
return CustomerName;
}
}
Unit Testing
We don’t have interfaces now, so stubs won’t work now, but we can use Shims. Shims are runtime method interceptors. They are more powerful than stubs. Using them we can add our own implementation to any method or property belonging to any available class in our own assembly or .net base class library.
Using Shims is considered as bad practice because you are using shims because your code is not following standards otherwise it would have allowed you generate stubs (not true always). Many time we have not left with any choice that using Shims.
Step 1 and Step 2 are going to be just same as above.
Step 3
Create a new Test Method and create Shims context inside it.
[TestMethod]
public void TestMethodWithShims()
{
//Arrange
CustomerMiddleLayer middleLayer = new CustomerMiddleLayer();
string CustomerName = "Sukesh Marla";
//Act
string returnCustomerName = null;
using (ShimsContext.Create())
{
ShimEmailLibrary.AllInstances.SendEmail = (@this) =>{ return true; };
returnCustomerName=middleLayer.AddCustomer(CustomerName);
}
//Assert
Assert.AreEqual<string>(CustomerName, returnCustomerName);
}
While using shims its must to define scoping region where call will be replaced with the help of ShimsContext.Create() block. Within the defined region every call to SendEmail function get replaced with call to SendEmail functionwhich we have defined (where we are doing nothing just returning true).
Shims for overriding environmental dependencies
Now let’s see a small demonstration where we will try to use environmental dependencies like DateTime.Now
First let’s create SUT – System under Test – Code which need to be tested
public class YearUtilities
{
publicbool IsTodayLastDateOfYear()
{
DateTime TodayDate = DateTime.Now;
if (TodayDate.Day == 31 && TodayDate.Month == 12)
{
return true;
}
else
{
return false;
}
}
}
Now let’s create Test Methods using normal way.
[TestMethod]
public void TestForLastDay()
{
//Arrange
YearUtilities yearUtilities = new YearUtilities();
//Act
bool CurrenrResult = yearUtilities.IsTodayLastDateOfYear();
//Assert
Assert.AreEqual<bool>(true, CurrenrResult);
}
[TestMethod]
public void TestForNotLastDay()
{
//Arrange
YearUtilities yearUtilities = new YearUtilities();
//Act
bool CurrenrResult = yearUtilities.IsTodayLastDateOfYear();
//Assert
Assert.AreEqual<bool>(false, CurrenrResult);
}
When you try to run this test methods, first test method will pass only on last day of year and Second method will pass all days except last day of year. This is not feasible in real life projects. We cannot wait till last day of year and then go and test our code.
Let’s rewrite above Test methods using Shims
Step 1
In the Test Project expand reference tab, right click assembly called System and say generate Fake assembly.
Step 2
Import namespace at the top as using System.Fakes;
Step 3
Write Test method as follows
[TestMethod]
public void TestForLastDay()
{
//Arrange
YearUtilities yearUtilities = new YearUtilities();
//Act
bool CurrenrResult = false;
using (ShimsContext.Create())
{
ShimDateTime.NowGet = () =>new DateTime(2012, 12,31);
CurrenrResult =yearUtilities.IsTodayLastDateOfYear();
}
//Assert
Assert.AreEqual<bool>(true, CurrenrResult);
}
[TestMethod]
public void TestForNotLastDay()
{
//Arrange
YearUtilities yearUtilities = new YearUtilities();
//Act
bool CurrenrResult = false;
using (ShimsContext.Create())
{
ShimDateTime.NowGet = () =>new DateTime(2012, 12,15);
CurrenrResult = yearUtilities.IsTodayLastDateOfYear();
}
//Assert
Assert.AreEqual<bool>(false, CurrenrResult);
}
Now when Test Methods are executed, DateTime.Now will return either 15 December 2012 or 31 December 2012 respectively. That means you can test your code throughout the year, anytime and anywhere.
At last:
Visual studio Fakes are really very powerful feature. When it comes to good practice stubs are at top but in some cases we won’t left with any choice than using shims.