In the last three posts of this series, I showed how to unit test Microsoft Dynamics CRM C# interfaces code with mock objects using Visual Studio 2012 and Moq. In this post, I will show how to unit test custom workflow activities that are executed by Dynamics CRM processes. I will be using the regular expression validation custom workflow activity I showed in my "Using regular expressions in Dynamics CRM 2011 processes" post.
Setting up the projects
First, you need to set up a project for the custom workflow activity. In Visual Studio create a new class library project called DemoCrmWorkflowActivities and add references to the following assemblies:
- System.Activities
- System.ServiceModel
- System.Runtime.Serialization
- System.Text.RegularExpressions
- Microsoft.Xrm.Sdk (browse to the .dll in the CRM SDK)
- Microsoft.Xrm.Sdk.Workflow (browse to the .dll in the CRM SDK)
Once that's done, you need to add a new unit test project to the solution. Go to the add project menu, and select the test->unit test project type. Following the <PROJECT-BEING-TESTED>.Test naming convention, the test project should be called DemoCrmWorkflowActivities.Test. Once that project is created, add references to the following assemblies:
- System.Runtime.Serialization
- Moq (browse to the Moq.dll you downloaded for the earlier examples)
- Microsoft.Xrm.Sdk (browse to the .dll in the CRM SDK)
- Microsoft.Xrm.Sdk.Workflow (browse to the .dll in the CRM SDK)
Also, make sure to add a reference to the DemoCrmWorkflowActivities project. Otherwise you won't be able to call the methods you want to test in your test methods.
The method to test
Because I am using a custom workflow activity that I had discussed in my "Using regular expressions in Dynamics CRM 2011 processes" post, I'm not going to go into the code in a lot of detail here. Basically, the custom workflow activity does the following:
- Accept a string to validate and a regular expression match pattern via input arguments.
- Evaluate the regular expression.
- Return the result of the match via an output argument.
The test method
In my test project, I have a method to test a valid phone number and another method to test an invalid phone number. Let's take a look at the code that makes up test for the valid phone number:
First, we can't run the custom workflow activity's Execute method directly, so we need to use a WorkflowInvoker object. We can set up the invoker like this:
//get new validateregex object
ValidateRegex valRegex = new ValidateRegex();
//instantiate the workflowinvoker
var invoker = new WorkflowInvoker(valRegex);
Because the custom workflow activity executes inside Dynamics CRM, the WorkflowInvoker object expects several extensions that we need to mock using Moq. They are:
- CRM tracing service
- CRM workflow context
- CRM organization service factory
Here's how we mock each of those:
//create our mocks
var serviceMock = new Mock<IOrganizationService>();
var factoryMock = new Mock<IOrganizationServiceFactory>();
var tracingServiceMock = new Mock<ITracingService>();
var workflowContextMock = new Mock<IWorkflowContext>();
//set up a mock service to act like the CRM organization service
IOrganizationService service = serviceMock.Object;
//set up a mock workflowcontext
var workflowUserId = Guid.NewGuid();
var workflowCorrelationId = Guid.NewGuid();
var workflowInitiatingUserId = Guid.NewGuid();
workflowContextMock.Setup(t => t.InitiatingUserId).Returns(workflowInitiatingUserId);
workflowContextMock.Setup(t => t.CorrelationId).Returns(workflowCorrelationId);
workflowContextMock.Setup(t => t.UserId).Returns(workflowUserId);
var workflowContext = workflowContextMock.Object;
//set up a mock tracingservice - will write output to console
tracingServiceMock.Setup(t => t.Trace(It.IsAny<string>(), It.IsAny<object[]>())).Callback<string, object[]>((t1, t2) => Console.WriteLine(t1, t2));
var tracingService = tracingServiceMock.Object;
//set up a mock servicefactory
factoryMock.Setup(t => t.CreateOrganizationService(It.IsAny<Guid>())).Returns(service);
var factory = factoryMock.Object;
We then add them to the WorkflowInvoker Extensions collection:
invoker.Extensions.Add<ITracingService>(() => tracingService);
invoker.Extensions.Add<IWorkflowContext>(() => workflowContext);
invoker.Extensions.Add<IOrganizationServiceFactory>(() => factory);
Once all the setup work is done, we can pass our input parameters and call the custom activity:
//set matchpattern to nanp format of xxx-xxx-xxxx
string matchPattern = @"^[2-9]\d{2}-\d{3}-\d{4}$";
//set string to validate to a valid phone number
string stringToValidate = "334-867-5309";
var inputs = new Dictionary<string, object>
{
{ "MatchPattern", matchPattern},
{ "StringToValidate", stringToValidate }
};
var outputs = invoker.Invoke(inputs);
int valid = Convert.ToInt16(outputs["Valid"]);
Finally, we use a simple assertion just like in the previous examples to check the output is as we expect:
//0 = invalid, 1 = valid
Assert.AreEqual(1, valid);
The code samples for this example can be downloaded here, and the testing code can be downloaded here.
In my next post, I’ll show how to unit test a Microsoft Dynamics CRM plug-in.
A version of this post was originally published on the HP Enterprise Services Application Services blog.