Dynamics CRM 2011 Unit Test Part 10: Microsoft Fakes with workflow activity

Dynamics CRM 2011 Unit Test Part 1: Introduction and Series Contents

The complete sample code using Microsoft Fakes can be downloaded from MSDN sample gallery: Dynamics CRM unit test using Microsoft Fakes

Unit test CRM workflow activity is similar to unit test CRM plugin in terms of the interface we have to mock. There are three distinct points in any code activity unit test, first is how to correctly invoke Execute method since it is protected, second is how to pass in the InArgument parameters and the last is how to pass in required service as extensions.

1. Correctly invoke CodeActivity.Execute(CodeActivityContext executionContext)

Since CodeActivity.Execute(CodeActivityContext executionContext) is protected, the correct way to invoke is method is to construct an instance of System.Activities.WorkflowInvoker and pass in an instance of custom code workflow activity. we can call the Invoke method on the WorkflowInvoker instance to trigger the execution of CodeActivity.Execute method.


AddMemberTeamActivity target = new AddMemberTeamActivity();

var invoker = new WorkflowInvoker(target);

var outputs = invoker.Invoke(inputs);

2. Correctly pass in InArgument<T> properties

the InArgument<T> properties need to be passed in as a dictionary when call WorkflowInvoker.Invoke method


var inputs = new Dictionary<string, object>
 {
 { "User", new EntityReference("systemuser", expectedUserId) },
 { "Team", new EntityReference("team", expectedTeamId) }
 };

var outputs = invoker.Invoke(inputs);

3. Pass in required service as extensions

required service can be added to WorkflowInvoker.Extensions property


var invoker = new WorkflowInvoker(target);
 invoker.Extensions.Add<ITracingService>(() => tracingService);
 invoker.Extensions.Add<IWorkflowContext>(() => workflowContext);
 invoker.Extensions.Add<IOrganizationServiceFactory>(() => factory);

Code Under Test


public sealed class AddMemberTeamActivity : CodeActivity
 {
 [RequiredArgument]
 [Input("User to Add")]
 [ReferenceTarget("systemuser")]
 public InArgument<EntityReference> User { get; set; }

[RequiredArgument]
 [Input("Team to Add To")]
 [ReferenceTarget("team")]
 public InArgument<EntityReference> Team { get; set; }

/// <summary>
 /// Executes the workflow activity.
 /// </summary>
 /// <param name="executionContext">The execution context.</param>
 protected override void Execute(CodeActivityContext executionContext)
 {
 // Create the tracing service
 ITracingService tracingService = executionContext.GetExtension<ITracingService>();

if (tracingService == null)
 {
 throw new InvalidPluginExecutionException("Failed to retrieve tracing service.");
 }

tracingService.Trace("Entered AddMemberTeamActivity.Execute(), Activity Instance Id: {0}, Workflow Instance Id: {1}",
 executionContext.ActivityInstanceId,
 executionContext.WorkflowInstanceId);

// Create the context
 IWorkflowContext context = executionContext.GetExtension<IWorkflowContext>();

if (context == null)
 {
 throw new InvalidPluginExecutionException("Failed to retrieve workflow context.");
 }

tracingService.Trace("AddMemberTeamActivity.Execute(), Correlation Id: {0}, Initiating User: {1}",
 context.CorrelationId,
 context.InitiatingUserId);

IOrganizationServiceFactory serviceFactory = executionContext.GetExtension<IOrganizationServiceFactory>();
 IOrganizationService service = serviceFactory.CreateOrganizationService(context.UserId);

try
 {
 EntityReference user = this.User.Get(executionContext);
 EntityReference team = this.Team.Get(executionContext);

if (!IsMemberInTeam(service, team.Id, user.Id))
 {
 OrganizationRequest request = new AddMembersTeamRequest { MemberIds = new Guid[] { user.Id }, TeamId = team.Id };

var response = service.Execute(request) as AddMembersTeamResponse;
 }
 }
 catch (FaultException<OrganizationServiceFault> e)
 {
 tracingService.Trace("Exception: {0}", e.ToString());

// Handle the exception.
 throw;
 }

tracingService.Trace("Exiting AddMemberTeamActivity.Execute(), Correlation Id: {0}", context.CorrelationId);
 }

internal bool IsMemberInTeam(IOrganizationService service, Guid teamId, Guid memberId)
 {
 OrganizationServiceContext context = new OrganizationServiceContext(service);

var query = from relationship in context.CreateQuery("teammembership")
 where relationship.GetAttributeValue<Guid>("teamid") == teamId
 && relationship.GetAttributeValue<Guid>("systemuserid") == memberId
 select relationship;

return query.FirstOrDefault() != null;
 }
 }

Unit Test


[TestClass]
 public class AddMemberTeamActivityTest
 {
 [TestMethod]
 public void ExecuteTest()
 {
 //
 // Arrange
 //
 Guid actualUserId = Guid.NewGuid();
 Guid actualTeamId = Guid.NewGuid();
 var workflowUserId = Guid.NewGuid();
 var workflowCorrelationId = Guid.NewGuid();
 var workflowInitiatingUserId = Guid.NewGuid();

// IOrganizationService
 var service = new Microsoft.Xrm.Sdk.Fakes.StubIOrganizationService();
 service.ExecuteOrganizationRequest = r =>
 {
 AddMembersTeamRequest request = r as AddMembersTeamRequest;
 actualUserId = request.MemberIds[0];
 actualTeamId = request.TeamId;
 return new AddMembersTeamResponse();
 };

// IWorkflowContext
 var workflowContext = new Microsoft.Xrm.Sdk.Workflow.Fakes.StubIWorkflowContext();
 workflowContext.UserIdGet = () =>
 {
 return workflowUserId;
 };
 workflowContext.CorrelationIdGet = () =>
 {
 return workflowCorrelationId;
 };
 workflowContext.InitiatingUserIdGet = () =>
 {
 return workflowInitiatingUserId;
 };

// ITracingService
 var tracingService = new Microsoft.Xrm.Sdk.Fakes.StubITracingService();
 tracingService.TraceStringObjectArray = (f, o) =>
 {
 Debug.WriteLine(f, o);
 };

// IOrganizationServiceFactory
 var factory = new Microsoft.Xrm.Sdk.Fakes.StubIOrganizationServiceFactory();
 factory.CreateOrganizationServiceNullableOfGuid = id =>
 {
 return service;
 };

var expectedUserId = Guid.NewGuid();
 var expectedTeamId = Guid.NewGuid();

AddMemberTeamActivity target = new AddMemberTeamActivity();
 using (ShimsContext.Create())
 {
 var fakeTarget = new DynamicsCRMUnitTest.Workflow.Fakes.ShimAddMemberTeamActivity(target);
 fakeTarget.IsMemberInTeamIOrganizationServiceGuidGuid = (svc, teamId, memberId) =>
 {
 return false;
 };

var invoker = new WorkflowInvoker(target);
 invoker.Extensions.Add<ITracingService>(() => tracingService);
 invoker.Extensions.Add<IWorkflowContext>(() => workflowContext);
 invoker.Extensions.Add<IOrganizationServiceFactory>(() => factory);

var inputs = new Dictionary<string, object>
 {
 { "User", new EntityReference("systemuser", expectedUserId) },
 { "Team", new EntityReference("team", expectedTeamId) }
 };

//
 // Act
 //
 var outputs = invoker.Invoke(inputs);
 }

//
 // Assert
 //
 Assert.AreEqual(expectedUserId, actualUserId);
 Assert.AreEqual(expectedTeamId, actualTeamId);
 }

[TestMethod]
 public void IsMemberInTeamTest()
 {
 //
 // Arrange
 //
 Guid userId = Guid.NewGuid();
 Guid teamId = Guid.NewGuid();
 bool expected = true;

AddMemberTeamActivity target = new AddMemberTeamActivity();

// IOrganizationService
 var service = new Microsoft.Xrm.Sdk.Fakes.StubIOrganizationService();
 service.ExecuteOrganizationRequest = r =>
 {
 List<Entity> entities = new List<Entity>
 {
 new Entity()
 };

var response = new RetrieveMultipleResponse
 {
 Results = new ParameterCollection
 {
 { "EntityCollection", new EntityCollection(entities) }
 }
 };

return response;
 };

//
 // Act
 //
 var actual = target.IsMemberInTeam(service, teamId, userId);

//
 // Assert
 //
 Assert.AreEqual(expected, actual);
 }
 }

Advertisements

2 comments on “Dynamics CRM 2011 Unit Test Part 10: Microsoft Fakes with workflow activity

  1. Pingback: Dynamics CRM 2011 Unit Test Part 1: Introduction and Series Contents | Zhongchen Zhou's Blog

  2. Pingback: Information to Get started with Unit Testing with Microsoft Fakes and CRM – Hosk's Dynamic CRM Blog

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s