CRM Plugin Unit Testing with Microsoft Fakes

Recently I got a chance to try my hands around writing unit tests for CRM Plugins. For this effort I used Microsoft Fakes framework. In order to ensure reusability of the code in each CRM plugin and help other developers to quickly adapt to writing unit tests for plugins, I came up with following classes. My vision is to use these classes as a framework for Plugin unit tests.

The classes are as below
1. ShimUtilityMethods – This is a wrapper class which provides shim methods for each utility method being used in the plugin. ShimUtilityMethods class definitions can be accessed at ShimUtilityMethods.cs.
2. ShimSDKMessages – This is a wrapper class which provides shim methods for each SDK messages (Create, Update, etc.) To bring more clarity to the user, I decided to separate out SDK message shim methods into a separate class. ShimSDKMessages class definitions can be accessed at ShimSDKMessages.cs.

How to use these in writing unit tests?

I will explain this with an example plugin. Consider a plugin which does attribute validation before the QualifyLead event. The plugin is registered at PreOperation event of the QualifyLead message. The plugin throws an exception terminating the process if any of the attribute is not present for qualification. Plugin code is as follows

public void Execute(IServiceProvider serviceProvider)
{
    try
{
        // Plugin Context code
 
// Get the Lead reference from the context
       EntityReference leadRef = (EntityReference)context.InputParameters[“LeadId”];
       // Retrieve Lead entity with all attributes
       lead = service.Retrieve(“lead”, leadRef.Id, new ColumnSet(true));
if (lead != null)
{
             //Validate required fields for qualification
ValidateRequiredFields(lead);
}
}/// <summary>
/// To validate required attributes of Lead for qualification
/// </summary>
/// <param name=”leadToValidate”>Lead</param>
private void ValidateRequiredFields(Entity leadToValidate)
{
       List<string> failedAttributes = new List<string>(); // stores the list of attributes which failed validation
       if (!leadToValidate.Attributes.Contains(“new_product”)) 
          failedAttributes.Add(“Product”);
       if (!leadToValidate.Attributes.Contains(“emailaddress1”))
          failedAttributes.Add(“Email”);
       if (!leadToValidate.Attributes.Contains(“address1_line1”)) 
          failedAttributes.Add(“Street 1”);
       if (!leadToValidate.Attributes.Contains(“numberofemployees”)) 
          failedAttributes.Add(“Number of Employees”);
       if (!leadToValidate.Attributes.Contains(“new_seats”))
          failedAttributes.Add(“Seats”);
       if (!leadToValidate.Attributes.Contains(“telephone1”))
failedAttributes.Add(“Mobile phone or Business Phone or Company Phone”);
if (failedAttributes.Count > 0)
{
           string failedAttributeList = null;
           for (int i = 0; i < failedAttributes.Count; i++)
{
failedAttributeList = failedAttributeList + failedAttributes[i];
               if (i != failedAttributes.Count – 1 && i != failedAttributes.Count – 2)
                  failedAttributeList = failedAttributeList + “, “;
               if (i == failedAttributes.Count – 2)
                  failedAttributeList = failedAttributeList + ” and “;
}
           // throw error and terminate the plugin execution
           throw new InvalidPluginExecutionException(“Following fields are required for the qualification of            Lead to an Opportunity – “ + failedAttributeList);
}
}}

 

Here’s a class which contains unit tests for the above plugin code. The class uses the 2 framework classes to write unit test code effectively.

[TestClass()]public class PreLeadQualificationTests

{

private static StubIServiceProvider fakeServiceProvider;

private static StubIOrganizationService fakeService;

[ClassInitialize]

public static void ClassInit(TestContext testContext)

{

fakeServiceProvider = ShimUtilityMethods.GetFakeServiceProvider();

var fakeContext = ShimUtilityMethods.GetFakePluginContext();

var fakeServiceFactory = ShimUtilityMethods.GetFakeServiceFactory(); // Fake Service Factory

fakeService = ShimUtilityMethods.GetFakeService();

}

enum TestScenario

{

ExecuteValidationExceptionTest1,

ExecuteValidationExceptionTest2

}

[TestMethod()]

public void ExecuteValidationExceptionTest2()

{

scenario = TestScenario.ExecuteValidationExceptionTest2;

using (ShimsContext.Create())

{

ShimUtilityMethods.ShimGetServiceType();

ShimUtilityMethods.ShimCreateOrganizationService();

ShimUtilityMethods.ShimInputParameters(ContextParameters);

ShimUtilityMethods.ShimPluginException();

ShimSDKMessages.ShimRetrieve(ValidateRequiredFieldsTestFewAttributes,fakeService);

var expectedMessage = string.Empty;

var actualMessage = string.Empty;

PreLeadQualification plugin = new PreLeadQualification();

plugin.Execute(fakeServiceProvider);

actualMessage = ShimUtilityMethods.actualMessage;

expectedMessage = “Following fields are required for the qualification of Lead to an Opportunity – Product family, Number of Employees and Estimated number of seats”;

Assert.AreEqual(expectedMessage, actualMessage);

}

}

private Entity ValidateRequiredFieldsTestAllAttributes(string entityName)

{

// Validate null values

Entity fakeLead1 = new Entity(entityName);

fakeLead1.Attributes[“mbs_productid”] = new EntityReference(“product”, Guid.Empty);

fakeLead1.Attributes[“emailaddress1”] = “noemail@thismail.com”;

fakeLead1.Attributes[“address1_line1”] = “line1”;

fakeLead1.Attributes[“numberofemployees”] = “500”;

fakeLead1.Attributes[“new_estimatednoofseats”] = “1000”;

fakeLead1.Attributes[“telephone1”] = “121231212”;

fakeLead1.Attributes[“companyname”] = “Test Company”;

fakeLead1.Attributes[“new_countryid”] = new EntityReference(“mbs_country”,Guid.Empty);

return fakeLead1;

}

}

The unit test class above uses the ShimUtilityMethods and ShimSDKMessages classes which provides shim methods for each service call made in the original plugin code. The call to those shim methods sets up the TestMethod with the required shims for a test scenario. Additionally,  the shim methods also gives an opportunity for the developer to define his own business logic to be returned when a particular shim method is executed.

For example, notice that ShimSDKMessages.ShimRetrieve method above requires 2 parameters, first parameter being a delegate Func<string,Entity> and the other being the fake service instance. This provides a shim for Retrieve SDK call.

Here the developer writing the unit test case is required to define a method matching the signature of the Func<> parameter which will be passed as a delegate to the ShimSDKMessages.ShimRetrive call and will be executed when the Retrieve call is hit in the original plugin code. In this way, based on the business logic of the plugin, the developer can define his own object to be returned for a retrieve call within his plugin code.

In the above code, ValidateRequiredFieldsTestFewAttributes is a method defined by the developer for his test scenario. Another plus point is the developer can return scenario-specific object using this method.

 

Advantages of using these classes

1. User/Developer writing unit tests doesn’t have to worry about writing the shim and stub methods for different functions/service calls in the plugin code. All necessary shim methods will be a part of ShimUtilityMethods and ShimSDKMessages classes. Developer can focus on core business logic in the plugin.
2. User/Developer can define functions which return exact objects based on their plugin business logic requirements. The shim methods defined in the helper classes executes these functions and return appropriate object whenever they are called in the original plugin code.

About swaroop41

http://rockstar365.com/swaroop41

Posted on November 25, 2014, in Uncategorized and tagged , , , , , , , . Bookmark the permalink. Leave a comment.

Leave a comment