How to unit test C# Dynamics CRM interface code - part II
Earlier this week I wrote a post that gave an introduction to unit testing Dynamics CRM C# interfaces code with mock objects using NUnit and Moq. The sample code in that post was extremely simple, so I wanted to follow up with a more complex example that shows how to test multiple calls to the CRM web service instead of just a single one.
This example supposes you have been asked to write a method that will take a company name as an input and then create a new CRM account. This method will also create a follow-up task linked to the account, and the subject of the task must contain an account number that is generated by a plug-in upon creation of the account.
First, let's think about how we would test this method before we start writing any code. After we have outlined our basic testing approach, writing the actual code will be much easier.
To test this method, we need to verify a few different things:
- We send CRM a create request that contains an account with the correct name value.
- We send CRM a create request that contains a tastk with the correct subject value (including the account number) and regardingobjectid value.
These two tests address the requirements, but they don't account for the reality that we need to make a retrieve request to CRM to get the account number upon account creation, so we should add a third test step to validate sending CRM a retrieve request for the account number using the account id value that is returned by creating the account.
Now that we have established what we have to test, we can write a method that makes the calls. It would look something like this:
public static void CreateCrmAccount2(string accountName, IOrganizationService service)
{
//create the account
Entity account = new Entity("account");
account["name"] = accountName;
Guid newId = service.Create(account);
//get the account number
account = service.Retrieve("account", newId, new Microsoft.Xrm.Sdk.Query.ColumnSet(new string[]{"name","accountid","accountnumber"}));
string accountNumber = account["accountnumber"].ToString();
//create the task
Entity task = new Entity("task");
task["subject"] = "Finish account set up for " + accountName + " - " + accountNumber;
task["regardingobjectid"] = new Microsoft.Xrm.Sdk.EntityReference("account", newId);
service.Create(task);
}
Retrieve( It.Is<string>(e => e.ToUpper() == "account".ToUpper()), It.Is<Guid>(e => e == idToReturn), It.IsAny<Microsoft.Xrm.Sdk.Query.ColumnSet>()) )
[Test]
public void CreateCrmAccount2()
{
//ARRANGE - set up everything our test needs
//first - set up a mock service to act like the CRM organization service
var serviceMock = new Mock<IOrganizationService>();
IOrganizationService service = serviceMock.Object;
//next - set a name and account number for our fake account record to create
string accountName = "Lucas Demo Company";
string accountNumber = "LPA1234";
//next - create a guid that we want our mock service Create method to return when called
Guid idToReturn = Guid.NewGuid();
//next - create an object that will allow us to capture the account object that is passed to the Create method
Entity createdAccount = new Entity();
//next - create an entity object that will allow us to capture the task object that is passed to the Create method
Entity createdTask = new Entity();
//next - create an mock account record to pass back to the Retrieve method
Entity mockReturnedAccount = new Entity("account");
mockReturnedAccount["name"] = accountName;
mockReturnedAccount["accountnumber"] = accountNumber;
mockReturnedAccount["accountid"] = idToReturn;
mockReturnedAccount.Id = idToReturn;
//finally - tell our mock service what to do when the CRM service methods are called
//handle the account creation
serviceMock.Setup(t =>
t.Create(It.Is<Entity>(e => e.LogicalName.ToUpper() == "account".ToUpper()))) //only match an entity with a logical name of "account"
.Returns(idToReturn) //return the idToReturn guid
.Callback<Entity>(s => createdAccount = s); //store the Create method invocation parameter for inspection later
//handle the task creation
serviceMock.Setup(t =>
t.Create(It.Is<Entity>(e => e.LogicalName.ToUpper() == "task".ToUpper()))) //only match an entity with a logical name of "task"
.Returns(Guid.NewGuid()) //can return any guid here
.Callback<Entity>(s => createdTask = s); //store the Create method invocation parameter for inspection later
//handle the retrieve account operation
serviceMock.Setup(t =>
t.Retrieve(
It.Is<string>(e => e.ToUpper() == "account".ToUpper()),
It.Is<Guid>(e => e == idToReturn),
It.IsAny<Microsoft.Xrm.Sdk.Query.ColumnSet>())
) //here we match on logical name of account and the correct id
.Returns(mockReturnedAccount);
//ACT - do the thing(s) we want to test
//call the CreateCrmAccount2 method like usual, but supply the mock service as an invocation parameter
MockDemo.CreateCrmAccount2(accountName, service);
//ASSERT - verify the results are correct
//verify the entity created inside the CreateCrmAccount method has the name we supplied
Assert.AreEqual(accountName, createdAccount["name"]);
//verify task regardingobjectid is the same as the id we returned upon account creation
Assert.AreEqual(idToReturn, ((Microsoft.Xrm.Sdk.EntityReference)createdTask["regardingobjectid"]).Id);
Assert.AreEqual("Finish account set up for " + accountName + " - " + accountNumber, (string)createdTask["subject"]);
}
This was originally posted here.

Like
Report
*This post is locked for comments