Unit Test or Test Driven Development for Dynamics 365 CE/CRM PlugIns - Custom Workflows


Life is easy with Test Driven Development even for Dynamics 365 PlugIns and Workflows. Any delta that be applied to the existent codes require test in order to see overall impact and ensure stability so each of the lines should be tested again,again,again with couple test cases.

My main target is to perform testing without CRM record generation in Dynamics CRM organization so I develop mock classes in order to simulate Dynamics CRM Data access layer thus Post or Pre operations just reach to my mock classes and send back necessary responses without accessing SQL Server Database.

I will explain all technical details below and share Project web page in github.com for sure.

First of all, I used power of Visual Studio 2019 community edition by creating "Unit Test Project (.NET Framework)" and downloading Microsoft.CrmSdk.CoreAssemblies Version 9.0.0.7 from NuGet in the study.

Let's start from introduction section as usual :)

Introduction

A plug-in is a custom business logic that integrates with Microsoft Dynamics CRM/CE to modify or extend the standard behavior of the platform. Plug-ins act as event handlers and are registered to execute on a particular event in CRM/CE. Plugins are written in either C# or VB and can run either in synchronous or asynchronous mode.

It is necessary to registered the custom code to Dynamics CRM/CE for Event Processing Framework. The Event Processing Framework in CRM/CE processes the synchronous and asynchronous plugin requests by passing it to the event execution pipeline. Whenever an event triggers a plugin logic, a message is sent to the CRM/CE Organization Web Service where it can be read or modified by other plugins or any core operations of the platform.

Unit Test for PlugIns

Any record manipulation occurs in Dynamics CRM, Engine checks registered custom codes as pre-operation and post-operation. If there are any codes block which inherited from IPlugIn (Microsoft.Xrm.Sdk). They are called by engine within an order.

Here is the entry point of our code for CRM engine:

public void Execute(IServiceProvider serviceProvider)

IServiceProvider is sent by CRM as a provider. for example; a sandbox provider then our codes are executed since we set up pointer of our class in there.

Do not worry, fortunately Dynamics CRM Developer Toolkit generates all necessary injenction classes and a class for only our extensions :) - feel free look at http://www.muhammetatalay.com/2019/10/dynamics-crm-365-solution-template-for.html
for details.

As i said before I do not want to perform my unit test class against Dynamics CRM organization in order to clean database from waste records. Because, We are not doing any tests to verify records at data rows level. Microsoft does all for us before go to market :) so We just need to verify if or business code blocks work as expected. Thus, I create Memory Based Service Provider in order to mock Dynamics CRM service provider to stop database access.

I build an interim class to manage the fake :).

I have 2 classes for that purpose;

1- InitializePlugIn.cs

A class where our unit test calls.

 public Entity Create(Entity entity, string stepName)

the method is caller of our PlugIn codes by passing Dynamics CRM database.
Entity : The name of the entity where PlugIn is bound
stepName : full name of the method (PlugIn Registration tool of Microsoft SDK gives the details)

Create method runs the given library via reflection class and invokes given method.

InitilaizePlugIn - Create 
2- MemoryServiceProvider.cs

It is responsible for generating Service provider for memory which will be used by the custom codes and libraries.


MemoryPluginExecutionContext is acting as an event handler and mocks all necessary variables and methods to be used by PlugIns.

following classes are generated for the illusion

class MemmoryExecutionContext : IExecutionContext

class ServiceEndpointNotificationService : IServiceEndpointNotificationService

class OrganizationService : IOrganizationService

class OrganizationServiceFactory : IOrganizationServiceFactory
 
How to test

You need to create unit test class in the Visual Studio Test Project and add PlugIn libraries of yours as a reference then create your scenarios.

for Example:

Suppose that there is business rule:
When a record is created for the CustomEntityA (bnb_customentityA) in Dynamics CRM, Name field of the record (bnb_name field) must be set by Date1 - Date2 - Type - Lookup1 - Price - Unit.

we can create a PlugIn then register it to Dynamics CRM for CustomEntityA entity thus, When a user creates a record for CustomEntityA. The code collects Date1, Date2, Type, Lookup1, Price, Unit from the record and update them as a name of record on Post-Operaion.


Unit test could be:

1- create an Entity for CustomEntityA
            Entity preEntity = new Entity("bnb_customentityA");

2- set fields as if user fills from Dynamics CRM User Interface
             preEntity.Id = Guid.NewGuid();
             preEntity.Attributes.Add("bnb_customentityAid", Guid.NewGuid());
         

            EntityReference refEntity = new EntityReference("bnb_lookup1");
            refEntity.Id = Guid.NewGuid();
            refEntity.Name = "Unit Test Lookup";
            preEntity.Attributes.Add("bnb_lookup1id", refEntity);
         
            preEntity.Attributes.Add("bnb_date1", DateTime.Now);
            preEntity.Attributes.Add("bnb_date2", DateTime.Now);
            ....
            ....
            ....
//we are now making a bridge in order to call MemoryServiceProcider for mocking
            Entity postEntity = plgIn.Create(preEntity, "TutorialPlugins.PostOperationbnb_customentityACreate");

//assert the result. postentiy which returns from the mock class must be same with one preentity.
            if (postEntity.Contains("bnb_name"))
                Assert.AreEqual(getName(preEntity), postEntity.Attributes["bnb_name"]);
            else
                Assert.Fail("return entity does not contain bnb_name field");

another sample:

Unit Test Sample


Here is working sample of mine . you can open the project from Visual Studio 2019 . You just need to debug and see step by step what are going on :)

https://github.com/TheMaty/Dynamcis365CEPlugInCustomWorkFlowUnitTest


do not hesitate to share your feedback

enjoy !




Comments

Popular posts from this blog

Assembly Microsoft.Dynamics.Service.Plugins.dll can not be loaded. Dynamics CRM 365 Engine version 9 - CRM User creation error

Exception caught instantiating TERADATA report server extension SQL Reporting Services

Could not load file or assembly 'System.ServiceModel, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089'. The system cannot find the file specified at Configuration class initiation in CrmServiceHelper.cv