XrmUnitTest and FakeXrmEasy are two testing frameworks that are specifically targeted towards Dynamics CRM/Dynamics 365. It is possible to do both unit test as well as integration tests using both these frameworks. In this first post, I would like to cover FakeXrmEasy. Here is a sample plugin code that I would like to test:
using Microsoft.Xrm.Sdk; using System; using Microsoft.Crm.Sdk.Messages; namespace SamplePlugin { public class LastNameUpperCasePlugin : IPlugin { public void Execute(IServiceProvider serviceProvider) { ITracingService tracer = (ITracingService)serviceProvider.GetService(typeof(ITracingService)); IPluginExecutionContext context = (IPluginExecutionContext)serviceProvider.GetService(typeof(IPluginExecutionContext)); IOrganizationServiceFactory factory = (IOrganizationServiceFactory)serviceProvider.GetService(typeof(IOrganizationServiceFactory)); IOrganizationService service = factory.CreateOrganizationService(context.UserId); try { Entity entity = (Entity)context.InputParameters["Target"]; //not required. Just added to demonstrate that we are connecting to a real crm org service var response = service.Execute(new WhoAmIRequest()); var lastName = entity.GetAttributeValue<string>("lastname"); if (!string.IsNullOrEmpty(lastName)) { entity["lastname"] = lastName.ToUpper(); } } catch (Exception e) { throw new InvalidPluginExecutionException(e.Message); } } } }
In order to test this plugin, we have to create a unit test project. Here are the steps for it:
- Create a unit test project from the existing plugin solution by right clicking and choose Add->New Project->Visual C#->Test->Unit Test Project
- Use nuget to add the correct FakeXrmEasy reference to the test project. In this example I am using “FakeXrmEasy.365” as I am connecting to a Dynamics 365 instance online. Refer https://www.nuget.org/profiles/jmontana to find out all the different versions that are relevant to your CRM version.
- Create a App.config file, if it doesn’t already exist. Here is my connection string. Refer https://msdn.microsoft.com/en-us/library/mt608573.aspx to find the correct format of the connection string for you crm instance.
Below is my test for the plugin
using System; using System.Collections.Generic; using System.Web.Configuration; using FakeXrmEasy; using Microsoft.VisualStudio.TestTools.UnitTesting; using Microsoft.Xrm.Sdk; namespace SamplePlugin.Test { [TestClass] public class LastNamePluginUnitTests { [TestMethod] public void Should_Set_LastName_In_UpperCase() { var context = new XrmRealContext { ProxyTypesAssembly = typeof(LastNameUpperCasePlugin).Assembly, ConnectionStringName = "CRMOnline" }; var executionContext = context.GetDefaultPluginContext(); var target = new Entity("contact") { ["lastname"] = "Power", Id = Guid.NewGuid() }; executionContext.MessageName = "Create"; executionContext.Stage = 20; executionContext.PrimaryEntityId = target.Id; executionContext.PrimaryEntityName = target.LogicalName; executionContext.InputParameters = new ParameterCollection { new KeyValuePair<string, object>("Target", target) }; context.ExecutePluginWith<LastNameUpperCasePlugin>(executionContext); Assert.AreEqual("POWER", ((Entity) executionContext.InputParameters["Target"]).GetAttributeValue<string>("lastname")); } } }
In this test I am creating a scenario where the plugin is running pre-create. You can change to add pre/post entity images or target a different message and stage. If we debug our integration test, we can see that we are connected to the real org service.
I have used latebound entity in this example, but you can use the same code with early bound entities generated using crmsvcutil or Early Bound Generator.
You can use a similar approach for testing custom workflow assemblies as well. In that case you would be using “GetDefaultWorkflowContext” instead of “GetDefaultPluginContext” to get the execution context.
Footnote: Technically this test could have been better written as an unit test instead of a integration test. But the purpose of this post is to demonstrate the points below:
- How to build a fake plugin execution context
- How to use connection strings to create a real OrganizationService instance
- How to use the real OrganizationService with the fake plugin execution context
EDIT (22/11/2016): Thank you Jonas (@rappen) for pointing out the plugin stage should be pre-create for this to work. Updated context stage to 20.