Problem Statement

Say there is an asp.net project to be unit tested. In an ASP.net app there will be methods to be unit tested that may have external dependencies like usage of Session variables, mapped path, databases, service calls etc.  The response from the external objects should not affect the unit test of a method. For example, the result of unit testing a method that makes a call to a service need not depend on the availability of the service. In such cases, we encapsulate those calls to the external objects into different methods. Then, these methods are mocked by extending the  Moles framework . In this case study we will showcase how some of the external objects as below can be mocked.

1. Session variables
2. Server.MapPath
3. Calls to WCF service
4. Writing  to event log
5. HtmlEncode and HtmlDecode
6. Read configuration values from web.config
7. Read files like .xml, .xls, etc.

The goal was to unit test all the methods in the project to achieve high code coverage that ensures code quality.
The goal of this document is to guide people Mock objects/methods or refactor the code in such a way that the code is testable. Also how Test Driven Development (TDD) and moles can complement each other.

Approach

The tool used to do the unit testing was Visual studio 2010 and “Moles” from Microsoft research. Moles allow you to replace any method with a custom lambda substitute.

 

Principles

The following two principles proved significant in achieving a code coverage as high as 80%:

1. Mock dependencies using ‘Moles’


a. Say a method ‘M1()’ is mocked, and when a call is made to M1() from a method being unit tested, the call is made to the mocked code for M1() instead of the actual code of M1().
For example,
Consider the below methods
Double CalculateDiscountedAmount(Double discAmt)
{
 Double totalTicketAmount = Convert.ToDouble(GetSession(“TicketAmount”));
 return totalTicketAmount - discAmt
}
Object GetSession(string sessionvar)
{
 return HttpContext.Current.Session[sessionvar];
}

[TestMethod]
[HostType(“Moles”)]
void CalculateDiscountedAmountTest()
{
 Math Target = new Math();
//mocked code for GetSession
MHelper.GetSessionDataString = (str) => { if(str==”TicketAmount”) return 100; else return 0;};
Double Actual  = Target.CalculateDiscountedAmount(10);
Assert.AreEqual(Actual,90);
}

In the above example, Target.CalculateDiscountedAmount(10) makes a call to ‘CalculateDiscountedAmount’ method, which further makes a call to ‘GetSession’. Notice that the GetSession() method call is replaced using the lambda expression below

“MHelper.GetSessionDataString = (str) => { if(str==”TicketAmount”) return 100; else return 0;};”

in CalculateDiscountedAmountTest() method, the call to the “GetSession” method from “CalculateDiscountedAmount” gets bypassed and the mocked code is executed.

2. Refactor the code in such a way that the code is divided in small modules.
For example we are using session variable. Create a method named “GetSession” and “SetSession”. Add the code to set object to Session or retrieve from Session in these methods. Call “GetSession” or “SetSession” instead of using session directly. In general

a. create a new method, say ‘Method1()’ and move the logic that cannot be mocked into ‘Method1()’
b. Mock the method ‘Method1()’ using Moles

This approach falls in line with the Test Driven Development – refactoring the code to make it unit testable.

 

Common Issues with Moles

The below are some of the common issues that might occur during implementation of Moles:

1. Problem #1:
Let’s continue with the example in Principle #1. We have mocked the method “GetSession” initially and the unit testing was working fine. Now there was a change in the signature and instead of taking 1 parameter it takes 2 parameters. Now the mocked code is obsolete and the mole assembly has to be regenerated to reflect the updated signature.


Solution:
 In order to update the moles assembly,


a. Delete ‘moleddependency.dll’ from the references
b. Right click on ‘dependency.dll’ and click on ‘add moles assembly’ – this would add ‘dependency.moles’ file
c. Now rebuild the project to generated the updated moles assembly ‘moleddependency.dll’
d. You can delete the ‘dependency.moles’ file

2. Problem #2:
If the following exception occurs:
InvalidOperationException: Dynamic operations can only be performed in homogenous AppDomain


Solution:
Go to Visual studio installation folder and look for the below file
C:\Program Files (x86)\Microsoft Visual Studio 10.0\Common7\IDE\PrivateAssemblies\Microsoft.Moles.VsHost.x86.exe.config
And change the config as below:


Examples illustrating cases where we implemented Moles

1. Set and get session variables

a. Assume we have created methods that get and set variables as below in a class ‘Helper’:


b, We can create a mole to the DLL that contains the above class and mock the above methods in the test method as below:

 

2. Server.MapPath

a., Assume we have created a method as below in a class ‘Helper’:

b. We can create a mole to the DLL that contains the above class and mock the above methods in the test method as below:

 

3. Make a call to WCF service

a. Create a mole assembly to the dll that contains a class for WCF client definition and mole the client constructor and client methods as below:

4. Make entries to event log

a. Assume there is a code as below that writes to event log in the method that needs to be unit tested

b. We can create a mole assembly to the dll that contains the logger class, ‘PCHWLogger’ in above example, and mock the method that writes to the event log and below

 

5. Using Server.HtmlEncode and Server.HtmlDecode

a. Assume we have created two methods as below in a class ‘Helper’:

b. We can create a mole to the DLL that contains the above class and mock the above methods in the test method as below

 

6. Read configuration values from web.config

a. If the method to be unit tested reads certain values from web.config, in the unit testing environment, we can mock the reading of config file by adding a moles assembly to the dll ‘WebConfigurationManager’

 

7. If the method to be unit tested makes use of a resource file, the same file can be used for unit testing by using [DeploymentItem]



REFERENCES

1. Download Pex and Moles

2. Pex and Moles on Facebook

3. Documentation on Pex and Moles