web
You’re offline. This is a read only version of the page.
close
Skip to main content

Notifications

Announcements

No record found.

Community site session details

Community site session details

Session Id :

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

Zhongchen Zhou Profile Picture Zhongchen Zhou 681

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


This was originally posted here.

Comments

*This post is locked for comments