Dynamics CRM 2011 Unit Test Part 9: Microsoft Fakes with CRM plugin

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 plugin is straightforward as well, it is just a bit tedious. IPlugin.Execute only accept one parameter which is IServiceProvider. Through IServiceProvider we can retrieve at least 3 interface and we need to set up 5 interface, which will be IOrganizationService, IPluginExecutionContext, ITracingService, IOrganizationServiceFactory and IServiceProvider.

Suppose we have a sample Dynamics CRM plugin, which is called after a contact is created within Dynamics CRM, if the parent customer is set to an account, the number of employees attribute of that account will be increased by one.

Code Under Test


public class Plugin : IPlugin
 {
 private string _unsecure, _secure;

/// <summary>
 /// Alias of the image registered for the snapshot of the
 /// primary entity's attributes after the core platform operation executes.
 /// The image contains the following attributes:
 /// parentcustomerid
 ///
 /// Note: Only synchronous post-event and asynchronous registered plug-ins
 /// have PostEntityImages populated.
 /// </summary>
 private readonly string postImageAlias = "PostCreateImage";

public Plugin(string unsecure, string secure)
 {
 _unsecure = unsecure;
 _secure = secure;
 }

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

if (pluginExecutionContext.Stage == 40 && pluginExecutionContext.MessageName == "Create" && pluginExecutionContext.PrimaryEntityName == "contact")
 {
 // Obtain the tracing service from the service provider.
 var tracingService = (ITracingService)serviceProvider.GetService(typeof(ITracingService));

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

// Use the factory to generate the Organization Service.
 var organizationService = factory.CreateOrganizationService(pluginExecutionContext.UserId);

Entity postImageEntity = (pluginExecutionContext.PostEntityImages != null && pluginExecutionContext.PostEntityImages.Contains(this.postImageAlias)) ? pluginExecutionContext.PostEntityImages[this.postImageAlias] : null;

// when a contact is created with parent account, increase number of employees for that account
 if (postImageEntity != null && postImageEntity.Contains("parentcustomerid"))
 {
 EntityReference parentCustomer = postImageEntity.GetAttributeValue<EntityReference>("parentcustomerid");

if (parentCustomer != null && parentCustomer.LogicalName.ToLowerInvariant() == "account")
 {
 tracingService.Trace("Parent account id: {0}.", parentCustomer.Id);

Entity parentAccount = organizationService.Retrieve("account", parentCustomer.Id, new Microsoft.Xrm.Sdk.Query.ColumnSet("numberofemployees"));

int numberOfEmployees = 0;
 if (parentAccount.Contains("numberofemployees"))
 {
 numberOfEmployees = parentAccount.GetAttributeValue<int>("numberofemployees");
 }

parentAccount["numberofemployees"] = numberOfEmployees + 1;

organizationService.Update(parentAccount);
 }
 }
 }
 }
 }

Unit Test


[TestClass]
 public class PluginTest
 {
 /// <summary>
 ///A test for Execute
 ///</summary>
 [TestMethod()]
 public void ExecuteTest()
 {
 //
 // Arrange
 //
 string unsecure = "unsecure";
 string secure = "secure";
 Plugin target = new Plugin(unsecure, secure);

var accountId = Guid.NewGuid();
 var previousNumber = 3;
 var expected = 4;
 var actual = 0;

// IOrganizationService
 var service = new Microsoft.Xrm.Sdk.Fakes.StubIOrganizationService();
 service.RetrieveStringGuidColumnSet = (entityName, id, columns) =>
 {
 return new Microsoft.Xrm.Sdk.Entity("account")
 {
 Id = accountId,
 Attributes = { { "numberofemployees", previousNumber } }
 };
 };
 service.UpdateEntity = (entity) =>
 {
 actual = entity.GetAttributeValue<int>("numberofemployees");
 };

// IPluginExecutionContext
 var pluginExecutionContext = new Microsoft.Xrm.Sdk.Fakes.StubIPluginExecutionContext();
 pluginExecutionContext.StageGet = () => { return 40; };
 pluginExecutionContext.MessageNameGet = () => { return "Create"; };
 pluginExecutionContext.PrimaryEntityNameGet = () => { return "contact"; };
 pluginExecutionContext.PostEntityImagesGet = () =>
 {
 return new EntityImageCollection
 {
 { "PostCreateImage", new Microsoft.Xrm.Sdk.Entity("contact")
 {
 Attributes = { { "parentcustomerid", new EntityReference("account", accountId) } }
 }
 }
 };
 };

// 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;
 };

// IServiceProvider
 var serviceProvider = new System.Fakes.StubIServiceProvider();
 serviceProvider.GetServiceType = t =>
 {
 if (t == typeof(IPluginExecutionContext))
 {
 return pluginExecutionContext;
 }
 else if (t == typeof(ITracingService))
 {
 return tracingService;
 }
 else if (t == typeof(IOrganizationServiceFactory))
 {
 return factory;
 }

return null;
 };

//
 // Act
 //
 target.Execute(serviceProvider);

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

Dynamics CRM 2011 Unit Test Part 8: Microsoft Fakes with IOrganizationService

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

Isolate IOrganizationService method call in unit test code should be very simple and straightforward, since it is a well defined interface. With Microsoft Fakes you can use the generated Stub types contained in the generated fake assembly.

Code Under Test


public class OrganizationServiceMethods
 {
 private IOrganizationService _service;

public OrganizationServiceMethods(IOrganizationService service)
 {
 _service = service;
 }

public IEnumerable<Guid> RetrieveAccountIdByName(string name)
 {
 QueryByAttribute query = new QueryByAttribute("account")
 {
 ColumnSet = new ColumnSet("accountid")
 };
 query.Attributes.Add("name");
 query.Values.Add(name);

var result = _service.RetrieveMultiple(query);

List<Guid> ids = new List<Guid>();

foreach (var entity in result.Entities)
 {
 ids.Add(entity.GetAttributeValue<Guid>("accountid"));
 }

return ids;
 }
 }

Unit Test


[TestMethod]
 public void RetrieveAccountIdByNameTest()
 {
 //
 // Arrange
 //
 IEnumerable<Guid> actual;
 IEnumerable<Guid> expected = new List<Guid> { Guid.NewGuid(), Guid.NewGuid() };

int callCount = 0;

var service = new Microsoft.Xrm.Sdk.Fakes.StubIOrganizationService();
 service.RetrieveMultipleQueryBase = (query) =>
 {
 callCount++;

var results = new EntityCollection();

results.Entities.AddRange(
 new Entity("account") { Attributes = { { "accountid", expected.ElementAt(0) } } },
 new Entity("account") { Attributes = { { "accountid", expected.ElementAt(1) } } });

return results;
 };

OrganizationServiceMethods target = new OrganizationServiceMethods(service);

//
 // Act
 //
 actual = target.RetrieveAccountIdByName("TestName");

//
 // Assert
 //
 Assert.AreEqual(callCount, 1); // verify OrganizationServiceContext.RetrieveVersion is called once

Assert.AreEqual(expected.Count(), actual.Count());
 Assert.AreEqual(expected.ElementAt(0), actual.ElementAt(0));
 Assert.AreEqual(expected.ElementAt(1), actual.ElementAt(1));
 }

Dynamics CRM 2011 Unit Test Part 7: Microsoft Fakes with CrmOrganizationServiceContext through IOrganizationService

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

In a previous post (Part 4: Microsoft Fakes with CrmOrganizationServiceContext), I have describe how to use run time redirect delegate feature of Microsoft Fakes to unit test CrmOrganizationServiceContext. I consider this a direct approach since you do not need to understand the IOrganizationService API and you do not need to know all the underlining messages. If you know IOrganizationService API and underlining messages, there is also an indirect approach to unit test CrmOrganizationServiceContext, which is to mock the underlining IOrganizationService.

CrmOrganizationServiceContext is SDK Extensions contained in Microsoft.Xrm.Client assembly. CrmOrganizationServiceContext allows the entity objects produced by the data context to participate in the WCF Data Services framework. CrmOrganizationServiceContext provide methods from various OrganizationRequest, which means a method based implementation of the message API. These methods are defined as extension methods, to use these methods we have to include the Microsoft.Xrm.Client.Messages namespace.

For more information on CrmOrganizationServiceContext, see Developer Extensions Context Object Model

CrmOrganizationServiceContext will perform all its method work through IOrganizationService.Execute method. We need to provide an instance of IOrganizationService to construct OrganizationServiceContext, OrganizationServiceContext is going to translate LINQ to CRM query into RetrieveMultipleRequest and execute that OrganizationRequest by calling IOrganizationService.Execute. If we mock IOrganizationService properly, we can isolate LINQ to CRM query unit test.

Code Under Test


public class CrmContextMethods2
 {
 private CrmOrganizationServiceContext _crmContext;

public CrmContextMethods2(IOrganizationService service)
 {
 _crmContext = new CrmOrganizationServiceContext(service);
 }

public string RetrieveVersion()
 {
 var version = _crmContext.RetrieveVersion();

return version;
 }

public Guid CreateAccount(string name)
 {
 var entity = new Entity("account")
 {
 Attributes = new Microsoft.Xrm.Sdk.AttributeCollection
 {
 { "name", name }
 }
 };

var id = _crmContext.Create(entity);

return id;
 }
 }

Unit Test


[TestClass]
 public class CrmContextMethods2Test
 {
 [TestMethod]
 public void CreateAccountTest()
 {
 //
 // Arrange
 //
 string accountName = "abcabcabc";
 Guid actual;
 Guid expected = Guid.NewGuid();

int callCount = 0;
 Entity entity = null;

var service = new Microsoft.Xrm.Sdk.Fakes.StubIOrganizationService();
 service.CreateEntity = e =>
 {
 callCount++;
 entity = e;
 return expected;
 };

CrmContextMethods2 target = new CrmContextMethods2(service);

//
 // Act
 //
 actual = target.CreateAccount(accountName);

//
 // Assert
 //
 Assert.AreEqual(callCount, 1); // verify OrganizationServiceContext.Create is called once
 Assert.IsNotNull(entity); // verify OrganizationServiceContext.Create is called with not null object
 Assert.AreEqual(entity.LogicalName, "account"); // verify OrganizationServiceContext.Create is called with entity with proper entity name
 Assert.AreEqual(entity.GetAttributeValue<string>("name"), accountName); // verify OrganizationServiceContext.Create is called with entity with proper value set on name attribute

Assert.AreEqual(expected, actual);
 }

[TestMethod]
 public void RetrieveVersionTest()
 {
 //
 // Arrange
 //
 string actual;
 string expected = "5.0.9690.2243";

int callCount = 0;

var service = new Microsoft.Xrm.Sdk.Fakes.StubIOrganizationService();
 service.ExecuteOrganizationRequest = r =>
 {
 callCount++;

return new RetrieveVersionResponse
 {
 Results = new ParameterCollection
 {
 { "Version", expected }
 }
 };
 };

CrmContextMethods2 target = new CrmContextMethods2(service);

//
 // Act
 //
 actual = target.RetrieveVersion();

//
 // Assert
 //
 Assert.AreEqual(callCount, 1); // verify OrganizationServiceContext.RetrieveVersion is called once

Assert.AreEqual(expected, actual);
 }

}

Part 6: Microsoft Fakes with OrganizationServiceContext through IOrganizationService

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

In a previous post (Part 3: Microsoft Fakes with OrganizationServiceContext), I have describe how to use run time redirect delegate feature of Microsoft Fakes to unit test OrganizationServiceContext. I consider this a direct approach since you do not need to understand the IOrganizationService API and you do not need to know all the underlining messages. If you know IOrganizationService API and underlining messages, there is also an indirect approach to unit test OrganizationServiceContext, which is to mock the underlining IOrganizationService.

We need to provide an instance of IOrganizationService to construct OrganizationServiceContext, if we mock IOrganizationService properly, we can isolate OrganizationServiceContext unit test.

Code under test


public class ContextMethods2
 {
 private OrganizationServiceContext _context;

public ContextMethods2(IOrganizationService service)
 {
 _context = new OrganizationServiceContext(service);
 }

public Guid CreateAccount(string name)
 {
 var entity = new Entity("account")
 {
 Attributes = new Microsoft.Xrm.Sdk.AttributeCollection
 {
 { "name", name }
 }
 };

_context.AddObject(entity);

var results = _context.SaveChanges();

return entity.Id;
 }
 }

Unit Test


[TestClass]
 public class ContextMethods2Test
 {
 [TestMethod]
 public void CreateAccountTest()
 {
 //
 // Arrange
 //
 string accountName = "abcabcabc";
 Guid actual;
 Guid expected = Guid.Empty;

int callCount = 0;
 Entity entity = null;

var service = new Microsoft.Xrm.Sdk.Fakes.StubIOrganizationService();
 service.ExecuteOrganizationRequest = r =>
 {
 callCount++;

var request = r as CreateRequest;
 entity = request.Target;
 expected = entity.Id;

return new CreateResponse
 {
 Results = new ParameterCollection
 {
 { "id", expected }
 }
 };
 };

ContextMethods2 target = new ContextMethods2(service);

//
 // Act
 //
 actual = target.CreateAccount(accountName);

//
 // Assert
 //
 Assert.AreEqual(callCount, 1); // verify OrganizationServiceContext.AddObject is called once
 Assert.IsNotNull(entity); // verify OrganizationServiceContext.AddObject is called with not null object
 Assert.AreEqual(entity.LogicalName, "account"); // verify OrganizationServiceContext.AddObject is called with entity with proper entity name
 Assert.AreEqual(entity.GetAttributeValue<string>("name"), accountName); // verify OrganizationServiceContext.AddObject is called with entity with proper value set on name attribute

Assert.AreEqual(expected, actual);
 }
 }

Dynamics CRM 2011 Unit Test Part 5: Microsoft Fakes with LINQ query through IOrganizationService

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

In a previous post (Part 2: Microsoft Fakes with LINQ query), I have describe how to use run time redirect delegate feature of Microsoft Fakes to unit test LINQ to CRM query. I consider this a direct approach since you do not need to understand the IOrganizationService API and you do not need to know all the underlining messages. If you know IOrganizationService API and underlining messages, there is also an indirect approach to unit test LINQ to CRM query, which is to mock the underlining IOrganizationService.

To use LINQ to query CRM data, we need to use OrganizationServiceContext or a deriving class such as CrmOrganizationServiceContext which is SDK Extensions contained in Microsoft.Xrm.Client assembly, or a deriving class created by the CrmSvcUtil tool. The OrganizationServiceContext class contains an underlying LINQ query provider that translates LINQ queries from Microsoft Visual C# or Microsoft Visual Basic .NET syntax into the query API used by Microsoft Dynamics CRM.

We need to provide an instance of IOrganizationService to construct OrganizationServiceContext, OrganizationServiceContext is going to translate LINQ to CRM query into RetrieveMultipleRequest and execute that OrganizationRequest by calling IOrganizationService.Execute. If we mock IOrganizationService properly, we can isolate LINQ to CRM query unit test.

Code under test


public class LINQToCRM2
 {
 private OrganizationServiceContext _context;

public LINQToCRM2(IOrganizationService service)
 {
 _context = new OrganizationServiceContext(service);
 }

public IEnumerable<Entity> RetrieveAccountsByName(string name)
 {
 var query = from account in _context.CreateQuery("account")
 where account.GetAttributeValue<string>("name") == name
 select account;

var accountList = new List<Entity>();

foreach (var entity in query)
 {
 accountList.Add(entity);
 }

//We can simply call query.ToList()
 //foreach loop here is to demonstrate item Enumeration

return accountList;
 }

public Entity RetrieveAccountsById(Guid id)
 {
 var query = from account in _context.CreateQuery("account")
 where account.GetAttributeValue<Guid>("accountid") == id
 select account;

var accountEntity = query.FirstOrDefault();

return accountEntity;
 }
 }

Unit Test


[TestClass]
 public class LINQToCRM2Test
 {
 [TestMethod]
 public void RetrieveAccountsByNameTest()
 {
 //
 // Arrange
 //
 var entity1 = new Microsoft.Xrm.Sdk.Entity("account");
 entity1.Id = Guid.NewGuid();
 entity1["name"] = "abcabcabc";

var entity2 = new Microsoft.Xrm.Sdk.Entity("account");
 entity2.Id = Guid.NewGuid();
 entity2["name"] = "123123123";

var entity3 = new Microsoft.Xrm.Sdk.Entity("account");
 entity3.Id = Guid.NewGuid();
 entity3["name"] = "a1b2c3a1b2c3";

var service = new Microsoft.Xrm.Sdk.Fakes.StubIOrganizationService();

service.ExecuteOrganizationRequest = r =>
 {
 List<Entity> entities = new List<Entity>
 {
 entity1, entity2
 };

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

return response;
 };

string accountName = "abcabcabc";
 IEnumerable<Microsoft.Xrm.Sdk.Entity> actual;
 LINQToCRM2 target = new LINQToCRM2(service);

//
 // Act
 //
 actual = target.RetrieveAccountsByName(accountName);

//
 // Assert
 //
 Assert.AreEqual(2, actual.Count());
 Assert.AreEqual(entity1, actual.ElementAt(0));
 Assert.AreEqual(entity2, actual.ElementAt(1));
 }

[TestMethod]
 public void RetrieveAccountsByIdTest()
 {
 //
 // Arrange
 //
 Guid id = Guid.NewGuid();

Microsoft.Xrm.Sdk.Entity expected = new Microsoft.Xrm.Sdk.Entity { Id = id };

var service = new Microsoft.Xrm.Sdk.Fakes.StubIOrganizationService();

service.ExecuteOrganizationRequest = r =>
 {
 List<Entity> entities = new List<Entity>
 {
 expected
 };

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

return response;
 };

Microsoft.Xrm.Sdk.Entity actual;
 LINQToCRM2 target = new LINQToCRM2(service);

//
 // Act
 //
 actual = target.RetrieveAccountsById(id);

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

Dynamics CRM 2011 Unit Test Part 4: Microsoft Fakes with CrmOrganizationServiceContext

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

If you know IOrganizationService API and underlining messages, there is also an indirect approach to unit test CrmOrganizationServiceContext, which is to mock the underlining IOrganizationService. That is discussed in another post (Part 7: Microsoft Fakes with CrmOrganizationServiceContext through IOrganizationService).

CrmOrganizationServiceContext

CrmOrganizationServiceContext is SDK Extensions contained in Microsoft.Xrm.Client assembly. CrmOrganizationServiceContext allows the entity objects produced by the data context to participate in the WCF Data Services framework. CrmOrganizationServiceContext provide methods from various OrganizationRequest, which means a method based implementation of the message API. These methods are defined as extension methods, to use these methods we have to include the Microsoft.Xrm.Client.Messages namespace.

For more information on CrmOrganizationServiceContext, see Developer Extensions Context Object Model

Microsoft Fakes does not support CrmOrganizationServiceContext, if we try to set up a run time delegate, we will receive ShimNotSupported exception. So we can go the traditional way to extract interface and provide a wrap type around CrmOrganizationServiceContext and implement that interface.

Most of the methods contained within SDK extension are OrganizationServiceContext extension methods. These methods are defined in Microsoft.Xrm.Client.Messages.OrganizationServiceContextExtensions and Microsoft.Xrm.Client.OrganizationServiceContextExtensions. And I have created one Interface and a wrapper Class.

https://gist.github.com/3180585

https://gist.github.com/3180591


namespace CrmOrganizationServiceContextExtensions
{
 public interface ICrmOrganizationServiceContext
 {
 /*
 * Microsoft.Xrm.Client.Messages.OrganizationServiceContextExtensions
 */

/*
 * Microsoft.Xrm.Client.CrmOrganizationServiceContext
 */
 }
}

 


namespace CrmOrganizationServiceContextExtensions
{
 public class CrmContext : CrmOrganizationServiceContext, ICrmOrganizationServiceContext
 {
 public CrmContext() : base() { }
 public CrmContext(string contextName) : base(contextName) { }
 public CrmContext(CrmConnection connection) : base(connection) { }
 public CrmContext(IOrganizationService service) : base(service) { }

private Microsoft.Xrm.Sdk.Client.OrganizationServiceContext _context;
 private Microsoft.Xrm.Sdk.Client.OrganizationServiceContext Context
 {
 get
 {
 if (_context == null)
 {
 _context = this as Microsoft.Xrm.Sdk.Client.OrganizationServiceContext;
 }

return _context;
 }
 }

/*
 * Microsoft.Xrm.Client.Messages.OrganizationServiceContextExtensions
 */

/*
 * Microsoft.Xrm.Client.OrganizationServiceContextExtensions
 */
 }
}

We can start using the Interface and wrapper Class in our code.

Suppose we have the following code under test


public class CrmContextMethods
 {
 private ICrmOrganizationServiceContext _crmContext;

public CrmContextMethods(ICrmOrganizationServiceContext crmContext)
 {
 _crmContext = crmContext;
 }

public string RetrieveVersion()
 {
 var version = _crmContext.RetrieveVersion();

return version;
 }

public Guid CreateAccount(string name)
 {
 var entity = new Entity("account")
 {
 Attributes = new Microsoft.Xrm.Sdk.AttributeCollection
 {
 { "name", name }
 }
 };

var id = _crmContext.Create(entity);

return id;
 }
 }

The unit test code code could be


[TestClass]
 public class CrmContextMethodsTest
 {
 [TestMethod]
 public void CreateAccountTest()
 {
 //
 // Arrange
 //
 string accountName = "abcabcabc";
 Guid actual;
 Guid expected = Guid.NewGuid();

int callCount = 0;
 Entity entity = null;

var context = new CrmOrganizationServiceContextExtensions.Fakes.StubICrmOrganizationServiceContext();
 context.CreateEntity = e =>
 {
 callCount++;
 entity = e;
 return expected;
 };

CrmContextMethods target = new CrmContextMethods(context);

//
 // Act
 //
 actual = target.CreateAccount(accountName);

//
 // Assert
 //
 Assert.AreEqual(callCount, 1); // verify OrganizationServiceContext.Create is called once
 Assert.IsNotNull(entity); // verify OrganizationServiceContext.Create is called with not null object
 Assert.AreEqual(entity.LogicalName, "account"); // verify OrganizationServiceContext.Create is called with entity with proper entity name
 Assert.AreEqual(entity.GetAttributeValue<string>("name"), accountName); // verify OrganizationServiceContext.Create is called with entity with proper value set on name attribute

Assert.AreEqual(expected, actual);

}

[TestMethod]
 public void RetrieveVersionTest()
 {
 //
 // Arrange
 //
 string actual;
 string expected = "5.0.9690.2243";

int callCount = 0;

var context = new CrmOrganizationServiceContextExtensions.Fakes.StubICrmOrganizationServiceContext();
 context.RetrieveVersion = () =>
 {
 callCount++;
 return expected;
 };

CrmContextMethods target = new CrmContextMethods(context);

//
 // Act
 //
 actual = target.RetrieveVersion();

//
 // Assert
 //
 Assert.AreEqual(callCount, 1); // verify OrganizationServiceContext.RetrieveVersion is called once

Assert.AreEqual(expected, actual);

}
 }

Dynamics CRM 2011 Unit Test Part 3: Microsoft Fakes with OrganizationServiceContext

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

If you know IOrganizationService API and underlining messages, there is also an indirect approach to unit test OrganizationServiceContext, which is to mock the underlining IOrganizationService. That is discussed in another post (Dynamics CRM 2011 Unit Test Part 6: Microsoft Fakes with OrganizationServiceContext through IOrganizationService).

OrganizationServiceContext

OrganizationServiceContext is based on the same concept as the ObjectContext Class in Entity Framework. The OrganizationServiceContext class lets you track changes, manage identities and relationships. Objects tracked by the organization service context are instances of entity types that represent data in Microsoft Dynamics CRM. You can designate actions to be performed on these objects and the service context tracks the changes. When the SaveChanges method is called, the service context instructs Microsoft Dynamics CRM to generate commands to create, update or delete the entities the tracked objects represent.

For more information on OrganizationServiceContext, see Use the Organization Service Context Class and OrganizationServiceContext Class

The main method that we want to simulate is OrganizationServiceContext.SaveChanges(). We will need to create fakes assembly for microsoft.xrm.sdk assembly using visual studio 2012, after that we can use Microsoft.Xrm.Sdk.Client.Fakes.ShimOrganizationServiceContext type contained within the fake assembly to detour call to OrganizationServiceContext.SaveChanges().

var fakeContext = new Microsoft.Xrm.Sdk.Client.Fakes.ShimOrganizationServiceContext(context);
fakeContext.SaveChanges = () =>
 {
 entity.Id = expected;

 // SaveChangesResultCollection only has one internal constructor
 // so we can not create a instance outside of Microsoft.Xrm.Sdk assembly
 // we will use reflection to create instances of SaveChangesResultCollection and SaveChangesResult
 var results = CreateSaveChangesResultCollection(SaveChangesOptions.None);

var request = new OrganizationRequest();
 var response = new OrganizationResponse();

results.Add(CreateSaveChangesResult(request, response));

return results;
 };

It should be very straightforward, except that SaveChangesResult and SaveChangesResultCollection only have internal constructors, we have to use reflection to create  SaveChangesResult and SaveChangesResultCollection that returned from OrganizationServiceContext.SaveChanges().

Create SaveChangesResultCollection

private SaveChangesResultCollection CreateSaveChangesResultCollection(SaveChangesOptions option)
 {
 var con = typeof(SaveChangesResultCollection).GetConstructors(BindingFlags.NonPublic | BindingFlags.Instance).FirstOrDefault();
 var results = con.Invoke(new object[] { option }) as SaveChangesResultCollection;
 return results;
 }

Create SaveChangesResult

private SaveChangesResult CreateSaveChangesResult(OrganizationRequest request, OrganizationResponse response)
 {
 var con = typeof(SaveChangesResult).GetConstructors(BindingFlags.NonPublic | BindingFlags.Instance).FirstOrDefault();
 var result = con.Invoke(new object[] { request, response }) as SaveChangesResult;
 return result;
 }

private SaveChangesResult CreateSaveChangesResult(OrganizationRequest request, Exception error)
 {
 var con = typeof(SaveChangesResult).GetConstructors(BindingFlags.NonPublic | BindingFlags.Instance).FirstOrDefault();
 var result = con.Invoke(new object[] { request, error }) as SaveChangesResult;
 return result;
 }

Suppose we use the following code to create account in crm

public class ContextMethods
 {
 private OrganizationServiceContext _context;

public ContextMethods(OrganizationServiceContext context)
 {
 _context = context;
 }

public Guid CreateAccount(string name)
 {
 var entity = new Entity("account")
 {
 Attributes = new Microsoft.Xrm.Sdk.AttributeCollection
 {
 { "name", name }
 }
 };

_context.AddObject(entity);

var results = _context.SaveChanges();

return entity.Id;
 }
 }

The unit test code could be

[TestClass]
 public class ContextMethodsTest
 {
 [TestMethod]
 public void CreateAccountTest()
 {
 //
 // Arrange
 //
 var connection = new CrmConnection("Crm");
 var service = new OrganizationService(connection);
 var context = new OrganizationServiceContext(service);
 using (ShimsContext.Create())
 {
 string accountName = "abcabcabc";
 Guid actual;
 Guid expected = Guid.NewGuid();

int callCount = 0;
 Entity entity = null;

var fakeContext = new Microsoft.Xrm.Sdk.Client.Fakes.ShimOrganizationServiceContext(context);

fakeContext.AddObjectEntity = e =>
 {
 callCount++;
 entity = e;
 };

fakeContext.SaveChanges = () =>
 {
 entity.Id = expected;

 // SaveChangesResultCollection only has one internal constructor
 // so we can not create a instance outside of Microsoft.Xrm.Sdk assembly
 // we will use reflection to create instances of SaveChangesResultCollection and SaveChangesResult
 var results = CreateSaveChangesResultCollection(SaveChangesOptions.None);

var request = new OrganizationRequest();
 var response = new OrganizationResponse();

results.Add(CreateSaveChangesResult(request, response));

return results;
 };

ContextMethods target = new ContextMethods(context);

//
 // Act
 //
 actual = target.CreateAccount(accountName);

//
 // Assert
 //
 Assert.AreEqual(callCount, 1); // verify OrganizationServiceContext.AddObject is called once
 Assert.IsNotNull(entity); // verify OrganizationServiceContext.AddObject is called with not null object
 Assert.AreEqual(entity.LogicalName, "account"); // verify OrganizationServiceContext.AddObject is called with entity with proper entity name
 Assert.AreEqual(entity.GetAttributeValue<string>("name"), accountName); // verify OrganizationServiceContext.AddObject is called with entity with proper value set on name attribute

 Assert.AreEqual(expected, actual);
 }
 }

private SaveChangesResultCollection CreateSaveChangesResultCollection(SaveChangesOptions option)
 {
 var con = typeof(SaveChangesResultCollection).GetConstructors(BindingFlags.NonPublic | BindingFlags.Instance).FirstOrDefault();
 var results = con.Invoke(new object[] { option }) as SaveChangesResultCollection;
 return results;
 }

private SaveChangesResult CreateSaveChangesResult(OrganizationRequest request, OrganizationResponse response)
 {
 var con = typeof(SaveChangesResult).GetConstructors(BindingFlags.NonPublic | BindingFlags.Instance).FirstOrDefault();
 var result = con.Invoke(new object[] { request, response }) as SaveChangesResult;
 return result;
 }

private SaveChangesResult CreateSaveChangesResult(OrganizationRequest request, Exception error)
 {
 var con = typeof(SaveChangesResult).GetConstructors(BindingFlags.NonPublic | BindingFlags.Instance).FirstOrDefault();
 var result = con.Invoke(new object[] { request, error }) as SaveChangesResult;
 return result;
 }
 }

Dynamics CRM 2011 Unit Test Part 2: Microsoft Fakes with LINQ query

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

If you know IOrganizationService API and underlining messages, there is also an indirect approach to unit test LINQ to CRM query, which is to mock the underlining IOrganizationService. That is discussed in another post (Dynamics CRM 2011 Unit Test Part 5: Microsoft Fakes with LINQ query through IOrganizationService).

LINQ to CRM query

Dynamics CRM 2011 supports using LINQ to query CRM data, which makes the query much more easy to write and compact. We do not need to construct a complex QueryExpression object for simple queries.

To use LINQ to query CRM data, we need to use OrganizationServiceContext or a deriving class such as CrmOrganizationServiceContext which is SDK Extensions contained in Microsoft.Xrm.Client assembly, or a deriving class created by the CrmSvcUtil tool. The OrganizationServiceContext class contains an underlying LINQ query provider that translates LINQ queries from Microsoft Visual C# or Microsoft Visual Basic .NET syntax into the query API used by Microsoft Dynamics CRM.

For more information on Build Queries with LINQ, please see Build Queries with LINQ (.NET Language-Integrated Query)

When test LINQ to CRM query, there are some challengers. First OrganizationServiceContext does not implement any interface or abstract class, second LINQ to CRM query involved some method in System.Linq namespace.


public class OrganizationServiceContext : IDisposable
public class CrmOrganizationServiceContext : OrganizationServiceContext, IInitializable, IOrganizationService, IUpdatable, IExpandProvider, IOrganizationServiceContainer
public partial class ServiceContext: Microsoft.Xrm.Sdk.Client.OrganizationServiceContext

The common method to test such class is to abstract an interface and use that interface in the calling code, but this method need to write more code and carefully design of the code. (This will be discussed in later posts with Rhino Mocks and Moq). A better approach is to use either Microsoft Fakes test framework or Microsoft Moles test framework. We will focus on Microsoft Fakes test framework in this post, Microsoft Moles will be introduced in later post.

Microsoft Fakes test framework is included in Visual Studio 2012 and can generate fakes assembly which contains shim types.  Shim types allow detouring of hard-coded dependencies on static or non-overridable methods.

For more information on Microsoft Fakes, please see Isolating Unit Test Methods with Microsoft Fakes

Let’s see a few unit test sample which use Microsoft Fakes

This sample use SDK Extensions contained in Microsoft.Xrm.Client assembly. For more information on Dynamics CRM SDK Extensions, please see SDK Extensions for Microsoft Dynamics CRM 2011 and Microsoft Dynamics CRM Online

We need to generate fake assembly for microsoft.xrm.sdk.dll and System.Core.dll

Enumeration

Suppose we have the following class


public class LINQToCRM
 {
 private OrganizationServiceContext _context;

public LINQToCRM(OrganizationServiceContext context)
 {
 _context = context;
 }

public IEnumerable RetrieveAccountsByName(string name)
 {
 var query = from account in _context.CreateQuery("account")
 where account.GetAttributeValue("name") == name
 select account;

var accountList = new List();

foreach(var entity in query)
 {
 accountList.Add(entity);
 }

//We can simply call query.ToList()
 //foreach loop here is to demonstrate item Enumeration

return accountList;
 }
}

There are two external dependencies in this code fragment. First is OrganizationServiceContext.CreateQuery, Second is foreach(var entity in query).

In this case we have to detour OrganizationServiceContext.CreateQuery and System.Linq.EnumerableQuery<Microsoft.Xrm.Sdk.Entity>.GetEnumerator to user defined delegate.

OrganizationServiceContext.CreateQuery is the place we can simulate Dynamics CRM data store. Entities returned from this method should be considered to be retrieved from Dynamics CRM, and the result will be further filtered by the rest of the LINQ operators.

System.Linq.EnumerableQuery<Microsoft.Xrm.Sdk.Entity>.GetEnumerator is the place we return the final result for the query, regardless of the data store or any LINQ operator.


[TestMethod]
 public void RetrieveAccountsByNameTest()
 {
 var connection = new CrmConnection("Crm");
 var service = new OrganizationService(connection);
 var context = new OrganizationServiceContext(service);
 using (ShimsContext.Create())
 {
 var fakeContext = new Microsoft.Xrm.Sdk.Client.Fakes.ShimOrganizationServiceContext(context);
 LINQToCRM target = new LINQToCRM(context);

var entity1 = new Microsoft.Xrm.Sdk.Entity("account");
 entity1.Id = Guid.NewGuid();
 entity1["name"] = "abcabcabc";

var entity2 = new Microsoft.Xrm.Sdk.Entity("account");
 entity2.Id = Guid.NewGuid();
 entity2["name"] = "123123123";

var entity3 = new Microsoft.Xrm.Sdk.Entity("account");
 entity3.Id = Guid.NewGuid();
 entity3["name"] = "a1b2c3a1b2c3";

fakeContext.CreateQueryString = (entityName) =>
 {
 return new System.Linq.EnumerableQuery(new Microsoft.Xrm.Sdk.Entity[] { entity1, entity2, entity3 });
 };

IEnumerable expected = new List { entity1, entity2 };

System.Linq.Fakes.ShimEnumerableQuery.AllInstances.GetEnumerator = (a) =>
 {
 return expected.GetEnumerator();
 };

string accountName = "abcabcabc";
 IEnumerable actual;
 actual = target.RetrieveAccountsByName(accountName);

Assert.AreEqual(expected.Count(), actual.Count());
 Assert.AreEqual(expected.ElementAt(0), actual.ElementAt(0));
 Assert.AreEqual(expected.ElementAt(1), actual.ElementAt(1));
 }
 }

FirstOrDefault method

If we do not need to enumerate the result but want to call FirstOrDefault method, we can detour System.Linq.Queryable.FirstOrDefault<Microsoft.Xrm.Sdk.Entity> to user defined delegate.

Suppose we have the following code under test


public class LINQToCRM
 {
 private OrganizationServiceContext _context;

public LINQToCRM(OrganizationServiceContext context)
 {
 _context = context;
 }

public Entity RetrieveAccountsById(Guid id)

{
 var query = from account in _context.CreateQuery("account")
 where account.GetAttributeValue("accountid") == id
 select account;

var accountEntity = query.FirstOrDefault();

return accountEntity;
 }
 }

The unit test would be similar to the following


[TestMethod]
 public void RetrieveAccountsByIdTest()
 {
 var connection = new CrmConnection("Crm");
 var service = new OrganizationService(connection);
 var context = new OrganizationServiceContext(service);
 using (ShimsContext.Create())
 {
 var fakeContext = new Microsoft.Xrm.Sdk.Client.Fakes.ShimOrganizationServiceContext(context);
 LINQToCRM target = new LINQToCRM(context);

fakeContext.CreateQueryString = (entityName) =>
 {
 return new System.Linq.EnumerableQuery(new Microsoft.Xrm.Sdk.Entity[] { });
 };

Guid id = Guid.NewGuid();

Microsoft.Xrm.Sdk.Entity expected = new Microsoft.Xrm.Sdk.Entity();

System.Linq.Fakes.ShimQueryable.FirstOrDefaultOf1IQueryableOfM0((a) =>
 {
 expected.Id = id;
 return expected;
 });

Microsoft.Xrm.Sdk.Entity actual;

actual = target.RetrieveAccountsById(id);

Assert.AreEqual(expected, actual);
 }
 }

Other common LINQ methods

Other common LINQ methods could also be find in System.Linq.Queryable class, so that we can use the generated shim class System.Linq.Fakes.ShimQueryable to detour required methods call to user defined delegate.

Note: Not all the methods are supported by Dynamics CRM. (e.g. Any is not supported)

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

Introduction

This is the first post of a series containing collections of patterns and code fragments showing how various unit frameworks can be used in Dynamics CRM 2011 development.

Unit test with server side components, (web service, plugin, workflow), will be explained with Microsoft Fakes, Rhino Mocks and Moq.

Unit test with client side JavaScript will be explained with QUnit.

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

Series Contents

Part 1: Introduction and Series Contents

  • Introduces the topics that will be covered in the series

Part 2: Microsoft Fakes with LINQ query

  • Unit test CRM LINQ query
  • Mock System.Linq namespace
  • Mock Mock OrganizationServiceContext.CreateQuery
  • Mock OrganizationServiceContext
  • Create SaveChangesResultCollection
  • Create SaveChangesResult

Part 4: Microsoft Fakes with CrmOrganizationServiceContext

  • Mock CrmOrganizationServiceContext

Part 5: Microsoft Fakes with LINQ query through IOrganizationService

  • Mock IOrganizationService to isolate LINQ to CRM query

Part 6: Microsoft Fakes with OrganizationServiceContext through IOrganizationService

  • Isolate method call to OrganizationServiceContext through mocking IOrganizationService

Part 7: Microsoft Fakes with CrmOrganizationServiceContext through IOrganizationService

  • Isolate method call to OrganizationServiceContext extension methods through mocking IOrganizationService
  • Isolate method call to CrmOrganizationServiceContext through mocking IOrganizationService

Part 8: Microsoft Fakes with IOrganizationService

  • Using Stub type to isolate method call to IOrganizationService

Part 9: Microsoft Fakes with CRM plugin

  •  Mock IOrganizationService, IPluginExecutionContext, ITracingService, IOrganizationServiceFactory and IServiceProvider

Part 10: Microsoft Fakes with workflow activity

  • Correctly invoke CodeActivity.Execute(CodeActivityContext executionContext)
  • Correctly pass in InArgument<T> properties
  • Pass in required service as extensions

Part 11: QUnit with client side JavaScript

  • QUnit
  • Xrm.Page Script Library Template (Microsoft Visual Studio extension)

Part 12: Rhino Mocks with LINQ query, OrganizationServiceContext and CrmOrganizationServiceContext

Part 13: Rhino Mocks with IOrganizationService

Part 14: Rhino Mocks with CRM plugin

Part 15: Rhino Mocks with workflow activity

Part 16: Moq with LINQ query, OrganizationServiceContext and CrmOrganizationServiceContext

Part 17: Moq with IOrganizationService

Part 18: Moq with CRM plugin

Part 19: Moq with workflow activity

Dynamics CRM 2011 check previous value in preimage in workflow

Sometimes it is required to check the previous value in a workflow triggered when record is updated.

The out of the box workflow create user interface only provide access to current value of the record which is the value after modification.

We could write one custom workflow activity to retrieve that value from IExecutionContext, which can be accessed from IWorkflowContext within custom workflow activity.


protected override void Execute(CodeActivityContext executionContext)
 {
IWorkflowContext context = executionContext.GetExtension<IWorkflowContext>();
Entity preImageEntity = context.PreEntityImages.Values.FirstOrDefault();
}

When use InOutArgument<EntityReference > in custom workflow activity, we need to specify the entity name which made it difficult to reuse, instead we could accept the attribute name of the lookup field and output entity name and record id for that lookup field. We can use entity name and record id in other workflow activity to perform generic tasks.

Here is a sample code to retrieve record id and entity name from custom workflow activity


public sealed class GetRecordIdEntityName : CodeActivity
 {
 [Input("Attribute Name for Related Record")]
 public InArgument<String> AttributeName { get; set; }

[Output("Primary Entity Name")]
 public OutArgument<String> PrimaryEntityName { get; set; }

[Output("Primary Record Id")]
 public OutArgument<String> PrimaryRecordId { get; set; }

[Output("Related Entity Name")]
 public OutArgument<String> RelatedEntityName { get; set; }

[Output("Pre Related Record Id")]
 public OutArgument<String> PreRelatedRecordId { get; set; }

[Output("Post Related Record Id")]
 public OutArgument<String> PostRelatedRecordId { 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 GetRecordId.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("GetRecordId.Execute(), Correlation Id: {0}, Initiating User: {1}",
 context.CorrelationId,
 context.InitiatingUserId);

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

try
 {
 // TODO: Implement your custom Workflow business logic.
 ExecuteCore(executionContext, context, service);
 }
 catch (FaultException<OrganizationServiceFault> e)
 {
 tracingService.Trace("Exception: {0}", e.ToString());

// Handle the exception.
 throw;
 }

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

private void ExecuteCore(CodeActivityContext executionContext, IWorkflowContext context, IOrganizationService service)
 {
 this.PrimaryRecordId.Set(executionContext, context.PrimaryEntityId.ToString());

this.PrimaryEntityName.Set(executionContext, context.PrimaryEntityName.ToLowerInvariant());

string attributeName = this.AttributeName.Get(executionContext);

this.RelatedEntityName.Set(executionContext, null);

if (!string.IsNullOrWhiteSpace(attributeName))
 {
 Entity preImageEntity = context.PreEntityImages.Values.FirstOrDefault();
 if (preImageEntity != null && preImageEntity.Contains(attributeName) && preImageEntity[attributeName] is EntityReference)
 {
 EntityReference entityRef = preImageEntity.GetAttributeValue<EntityReference>(attributeName);
 this.RelatedEntityName.Set(executionContext, entityRef.LogicalName.ToLowerInvariant());
 this.PreRelatedRecordId.Set(executionContext, entityRef.Id.ToString());
 }
 else
 {
 this.PreRelatedRecordId.Set(executionContext, null);
 }

Entity postImageEntity = context.PostEntityImages.Values.FirstOrDefault();
 if (postImageEntity != null && postImageEntity.Contains(attributeName) && postImageEntity[attributeName] is EntityReference)
 {
 EntityReference entityRef = postImageEntity.GetAttributeValue<EntityReference>(attributeName);
 this.RelatedEntityName.Set(executionContext, entityRef.LogicalName.ToLowerInvariant());
 this.PostRelatedRecordId.Set(executionContext, entityRef.Id.ToString());
 }
 else
 {
 this.PostRelatedRecordId.Set(executionContext, null);
 }
 }
 else
 {
 this.PreRelatedRecordId.Set(executionContext, null);
 this.PostRelatedRecordId.Set(executionContext, null);
 }
 }
 }