Monday, July 6, 2020

Unit Testing Dynamics 365 CE (CRM) Plugins/Actions

Unit testing is very important concept which ensure your code work without error and fulfill the requirement. Unit testing CRM plugins/actions are challenging because plugins and actions are executing under CRM context. If you create unit test project against CRM plugin project you have to mock CRM plugin context to unit test. Mocking CRM plugin context is not an easy task because it need lots of coding and it will be adding additional burden to the development.

There is a tool called “FakeXrmEasy” (https://dynamicsvalue.com/home) will remove that extra burden from development team and fake the CRM context. Using fake Xrm context we can unit test CRM plugins and actions without deploying them to CRM. This will help to debug CRM plugins as well.

Unit testing using FakeXrmEasy
I have create plugin project called “CEAutomation” and MSTest unit test project called “PluginUnitTest”.



I used MSTest (.Net Framework) as unit test project but there are several unit test frameworks available like NUnit, xUnit, etc.



Once you create unit test project you have to add FakeXrmEasy assemblies as NuGet Package. It will add Core CRM assemblies and necessary NuGet packages to your project.

Note: Please make sure you add correct version of FakeXrmEasy because there are several NuGet packages available for different versions of CRM.

In my case I use D365 CE online version, due to that reason I installed CRM 365 version supported version.


Test Scenario:
We have an online exam system which conduct exams through internet. After exam each and every candidate should fill out survey which collect their feedback about exam. Once they complete the survey, system will extract data for report. We implement CRM action to extract survey responses by sending survey id as an input parameter. Once action invoked with valid survey id, it query the CRM and retrieve all responses then convert responses to JSON string and return as output parameter.

We are going to create unit test for that CRM action. We are going to use FakeXrmEasy to create unit test and use XrmRealContext to connect to the CRM.

If our plugin implemented correctly once we feed valid CRM survey record id it should return the available responses list as JSON.

Plugin Code:

namespace CEAutomation.Response
{
    public class ResponseHandler : IPlugin
    {
        public IServiceProvider ServiceProvider
        {
            get;

            set;
        }

        public IOrganizationService OrganizationService
        {
            get;

            set;
        }

        public IPluginExecutionContext PluginExecutionContext
        {
            get;

            set;
        }

        public ITracingService TracingService
        {
            get;

            set;
        }

        public void Execute(IServiceProvider serviceProvider)
        {
            // Obtain the execution context service from the service provider.
            PluginExecutionContext = (IPluginExecutionContext)serviceProvider.GetService(typeof(IPluginExecutionContext));

            // Obtain the tracing service from the service provider.
            TracingService = (ITracingService)serviceProvider.GetService(typeof(ITracingService));

            // Obtain the Organization Service factory service from the service provider
            IOrganizationServiceFactory factory = (IOrganizationServiceFactory)serviceProvider.GetService(typeof(IOrganizationServiceFactory));

            OrganizationService = factory.CreateOrganizationService(PluginExecutionContext.UserId);

            if (PluginExecutionContext.InputParameters.Contains("SurveyId")
&& Guid.TryParse(PluginExecutionContext.InputParameters["SurveyId"].ToString(), out Guid surveyId) 
&& surveyId != Guid.Empty)

            {
                Survey.Survey survey = new Survey.Survey();
//Do What you want
                var responses = survey.GetSurveyResponsesJsonString(OrganizationService, TracingService, surveyId);
                PluginExecutionContext.OutputParameters["Responses"] = responses;
            }
        }

    }
}


Adding App.config to unit test project

Visual Studio not allowed to add Application Configuration file (App.config) file to unit test. We need to add it manually because we need to provide CRM connection string to XrmRealContext create connection. Since our project library does not contains App.config file we have to create it then include it to project using visual studio and set property to copy it to output directory. Add connection string as following and set copy to output directory property true in file properties.

<?xml version="1.0" encoding="utf-8" ?>
<configuration>
  <connectionStrings>
    <add name="SurveyCRMDevInstance" connectionString="AuthType = Office365; Url = https://org.crm.dynamics.com/;Username=testusername@testdomain.onmicrosoft.com;Password=password" />
  </connectionStrings>
</configuration>


We named our CRM instance connection string as

SurveyCRMDevInstance”. You have to use same name when you initiate XrmRealContext.
To invoke plugin we have to supply survey id as input parameter named “SurveyId”. Once plugin executed it returns data as output parameter named “Responses”. In unit test we have to supply valid survey id as input parameter and we can extract result using output parameter.

Following code snippet will show how create unit test for action with input and output parameters.

[TestMethod]
public void RetrieveResponsesTest()
{
    try
    {
        var context = new XrmRealContext
        {
            ProxyTypesAssembly = typeof(ResponseHandler).Assembly,
            ConnectionStringName = "SurveyCRMDevInstance"
        };

        Guid.TryParse("9eab7684-fbb5-ea11-a812-000d3a305386", out Guid conductedSurveyId);

        var executionContext = context.GetDefaultPluginContext();
        executionContext.MessageName = "ResponseHandler";
        executionContext.InputParameters = new ParameterCollection
        {
            new KeyValuePair<string, object>("SurveyId", conductedSurveyId)
        };
        context.ExecutePluginWith<ResponseHandler>(executionContext);
        string outputString = executionContext.OutputParameters["Responses"].ToString();

        if (string.IsNullOrEmpty(outputString))
        {
            Assert.Fail("No result");
        }
        else
        {
            List<ebecssvy_responses> responseObjList = Utility.JsonToObject<List<ebecssvy_responses>>(outputString);

            Assert.AreNotEqual(0, responseObjList.Count, "List Contains Data");
        }

    }catch (Exception ex)
    {
        Assert.Fail(ex.Message);
    }
}



This can extend to unit test any plugin/action/custom workflow. We will post those special scenarios in future posts.
Happy Coding 😊

No comments:

Post a Comment