Unit Testing Using Fakes For MS Dynamics CRM Plugins

When you finished your Dynamics CRM Plugin you need to write a Unit test to achieve 100% code coverage. Writing unit tests can be difficult, time-consuming, and slow when you can't isolate the classes you want to test from the rest of the system. Here are some steps you need to follow and cover your plugin code using Unit test methods.
 
Here in this blog, I have explained how to write unit test method for different functionality, data types and events like Retrieve, Retrieve Multiple, PreImage data, Post Image data, Entity, EntityReference, Option set,  TargetEntity data, etc.
 
How to create a Unit test project: Create Unit test project in VS 2019
 
Step 1
 
Add Fakes references to your MS project
 
Unit testing using Fakes for MS Dynamics CRM Plugins
 
Unit testing using Fakes for MS Dynamics CRM Plugins 
 
The Fakes Assemblies will automatically be created in your project.
 
Now you can start writing your Unit test method code.
 
Always remember Unit test for the plugin is passing hardcoded value only. The plugin does not connect with the CRM database while writing the Unit test methods.
 
Step 2
 
Use Fakes libraries in your Unittest.cs file
 
Unit testing using Fakes for MS Dynamics CRM Plugins
 
Now the below code is for every test method will be same. It contains the declaration, Initialization of the IPlugin interface.
  1. /// <summary>    
  2. /// Organization Service Response    
  3. /// </summary>    
  4. private OrganizationResponse response = new CreateResponse();    
  5.   
  6. /// <summary>    
  7. /// Gets or sets Service Provider    
  8. /// </summary>    
  9. private static StubIServiceProvider ServiceProvider { getset; }    
  10.   
  11. /// <summary>    
  12. /// Gets or sets Plugin Execution Context    
  13. /// </summary>    
  14. private static StubIPluginExecutionContext PluginExecutionContext { getset; }    
  15.   
  16. /// <summary>    
  17. /// Gets or sets Organization Service    
  18. /// </summary>    
  19. private static StubIOrganizationService OrganizationService { getset; }    
  20.   
  21. /// <summary>    
  22. /// Gets or sets Test Entity    
  23. /// </summary>    
  24. private Entity TestEntity { getset; }    
  25.   
  26. /// <summary>    
  27. /// Class Initialize    
  28. /// </summary>    
  29. /// <param name="textContext">text Context</param>    
  30. [ClassInitialize]    
  31. public static void ClassInit(TestContext textContext)    
  32. {    
  33.     var context = new StubIPluginExecutionContext();    
  34.     var tracingService = new StubITracingService();    
  35.     var orgFactory = new StubIOrganizationServiceFactory();    
  36.     ServiceProvider = new StubIServiceProvider();    
  37.     OrganizationService = new StubIOrganizationService();    
  38.     PluginExecutionContext = context;    
  39.   
  40.     ////override GetService behaviour and return our stubs    
  41.     ServiceProvider.GetServiceType = (type) =>    
  42.     {    
  43.         if (type == typeof(IPluginExecutionContext))    
  44.         {    
  45.             return context;    
  46.         }    
  47.         else if (type == typeof(IOrganizationServiceFactory))    
  48.         {    
  49.             return orgFactory;    
  50.         }    
  51.         else if (type == typeof(ITracingService))    
  52.         {    
  53.             return tracingService;    
  54.         }    
  55.         else if (type == typeof(IOrganizationService))    
  56.         {    
  57.             return OrganizationService;    
  58.         }    
  59.   
  60.         return null;    
  61.     };    
  62.        
  63.     ////return our stub organizationservice    
  64.     orgFactory.CreateOrganizationServiceNullableOfGuid = (userId) => OrganizationService;    
  65.   
  66.     ////write trace logs to output. only works when debugging tests    
  67.     tracingService.TraceStringObjectArray = (message, args) => Debug.WriteLine(message, args);    
  68. }    
  69.   
  70. /// <summary>    
  71. /// Initialize Test File    
  72. /// </summary>    
  73. [TestInitialize]    
  74. public void TestInit()    
  75. {    
  76.     ////setup initial values for each test    
  77.     var inputParameters = new ParameterCollection();    
  78.     PluginExecutionContext.InputParametersGet = () => inputParameters;    
  79.     this.TestEntity = new Entity();    
  80.     inputParameters.Add(new KeyValuePair<stringobject>("Target"this.TestEntity));    
  81. }    
  82.   
  83. /// <summary>    
  84. /// Test Cleanup    
  85. /// </summary>    
  86. [TestCleanup]    
  87. public void TestCleanup()    
  88. {    
  89.     this.TestEntity = null;    
  90. } 
Step 3
 
Write your Unit test methods
  1. [TestMethod]      
  2. public void UnitTestMethod()      
  3. {      
  4.     ////Arrange      
  5.     ParameterCollection parameter;      
  6.   
  7.     ////setup input parameters.       
  8.     PluginExecutionContext.InputParametersGet = () =>      
  9.     {      
  10.         parameter = new ParameterCollection      
  11.         {      
  12.             ["Target"] = new Entity("account", Guid.Parse("e910c8ae-4c9d-e911-a98f-002248005d8"))      
  13.             {      
  14.             }      
  15.         };      
  16.         return parameter;      
  17.     };      
  18.   
  19.     PluginExecutionContext.PostEntityImagesGet = () =>      
  20.     {      
  21.         // new entity      
  22.         Entity contactEntity = new Entity("contact", Guid.NewGuid());      
  23.   
  24.         // Entity image object collection      
  25.         EntityImageCollection parameterImage = new EntityImageCollection();      
  26.   
  27.         // Lookup field       
  28.         EntityReference entityReference = new EntityReference      
  29.         {      
  30.             Id = Guid.NewGuid(),      
  31.             LogicalName = "opportunity"      
  32.         };      
  33.   
  34.         contactEntity["opportunityid"] = entityReference;      
  35.               
  36.         // Option set field      
  37.         contactEntity["advertise"] = new OptionSetValue(3);      
  38.   
  39.         // Date time field      
  40.         contactEntity["starttime"] = DateTime.Now;      
  41.               
  42.         // Post Image data      
  43.         parameterImage["PostImage"] = contactEntity;      
  44.   
  45.         return parameterImage;      
  46.     };      
  47.   
  48.     PluginExecutionContext.PreEntityImagesGet = () =>      
  49.     {      
  50.             
  51.         EntityImageCollection parameterImage = new EntityImageCollection();      
  52.         parameterImage["PreImage"] = new Entity();      
  53.         return parameterImage;      
  54.     };      
  55.   
  56.     // Retrieve based entityId      
  57.     OrganizationService.RetrieveStringGuidColumnSet = (entityName, id, columns) =>      
  58.         {      
  59.             var entity = new Entity(entityName)      
  60.             {      
  61.                 Id = id      
  62.             };      
  63.             entity.Attributes["date"] = DateTime.Now.AddHours(2);      
  64.             entity.Attributes["dateTime"] = DateTime.Now.AddHours(4);                     
  65.             return entity;      
  66.         };      
  67.   
  68.        // Retrieve multiple      
  69.         OrganizationService.RetrieveMultipleQueryBase = (req) =>      
  70.         {      
  71.             EntityCollection ec = null;      
  72.             if (req is FetchExpression)      
  73.             {      
  74.                 var fe = req as FetchExpression;      
  75.                 if (fe.Query.Contains("msdyn_timegroupdetail"))      
  76.                 {      
  77.                     ec = new EntityCollection();      
  78.                     ec.Entities.Add(new Entity()      
  79.                     {      
  80.                         Attributes = new AttributeCollection()      
  81.                         {      
  82.                         { "msdyn_starttime", DateTime.Now.AddHours(2)},      
  83.                         { "msdyn_endtime", DateTime.Now.AddHours(3)},      
  84.                         }      
  85.                     });      
  86.                 }      
  87.             }      
  88.             if (req is QueryExpression)      
  89.             {      
  90.                 ec = new EntityCollection();      
  91.                 ec.Entities.Add(new Entity()      
  92.                 {      
  93.                     Attributes = new AttributeCollection()      
  94.                         {      
  95.                         { "timezonecode", 85},      
  96.                         }      
  97.                 });      
  98.             }      
  99.             return ec;      
  100.         };      
  101.        PluginExecutionContext.StageGet = () => 40;      
  102.        PluginExecutionContext.MessageNameGet = () => "Update";      
  103.        PluginExecutionContext.PrimaryEntityNameGet = () => "account";      
  104.        UnittestPlugin unitTestPlugin = new UnittestPlugin();      
  105.        unitTestPlugin.Execute(ServiceProvider);      
  106. } 
Using the above code you can easily cover your code coverage for your code.
 
Somewhere in the code, we needed a plugin exception for code coverages. Using the below code you can cover the IPluginExecutionException, FaultException, Exception for the exception handling process in the Unit test.
  1. [TestMethod, ExpectedException(typeof(InvalidPluginExecutionException))]    
  2. public void PluginException()    
  3. {    
  4.     ////Arrange    
  5.     ParameterCollection parameter;    
  6.   
  7.     //// put wrong GUID in entity GUID it will show you IPluginExecutionException     
  8.     PluginExecutionContext.InputParametersGet = () =>    
  9.     {    
  10.         parameter = new ParameterCollection    
  11.         {    
  12.             ["Target"] = new Entity("account"new Guid("ed33f83c-9ed3-e911-a813-000d3a6d652"))    
  13.             {    
  14.                 Attributes = {    
  15.                         
  16.                 }    
  17.             }    
  18.         };    
  19.         return parameter;    
  20.     };    
  21. } 
For Fault Exception put null values or make cast error,
  1.   [TestMethod, ExpectedException(typeof(InvalidCastException))]    
  2.   public void LoggedInUserDoesNotHaveSysAdminRole()    
  3.   {    
  4.       //// Now Here "accountcategory" code field is an Option set field but we are passing     
  5.       //// string value to the field So it will generate an error    
  6.       PluginExecutionContext.PreEntityImagesGet = () =>    
  7.       {    
  8.           EntityImageCollection preImage = new EntityImageCollection    
  9.           {    
  10.               ["PreImage"] = new Entity("account")    
  11.               {    
  12.                   Attributes = {                                
  13.                       { "accountcategorycode""value" }    
  14.                   }    
  15.               }    
  16.           };    
  17.           return preImage;    
  18.       };    
  19. } 
I hope this article will solve your problem with writing a Unit test using Fakes.
 
Keep learning new things.....