A while back I wrote a post that gave a high-level overview of some of the tools I had used at a previous job to do unit testing of Dynamics CRM interfaces code, but I didn't get into the why or how. Here is an introduction to unit testing Dynamics CRM code using an automated unit testing framework and a mock object framework.
First, let's cover the "why." When writing code that interacts with Dynamics CRM, it is important to:
- Unit test your code
- Use an automated unit testing framework to automate your unit tests
- Write unit tests that do not depend on an actual CRM server
These points apply to any situation in which you are writing code that interacts with an external system, but my example is focused on Dynamics CRM and assumes you have some level of familiarity with it, the Dynamics CRM SDK, etc.
Why unit test?
According to the Wikipedia entry on unit testing, there are five main benefits:
- You will find defects earlier in your development cycle.
- Unit tests make for easier refactoring and other code changes.
- A comprehensive set of unit tests will make integration testing easier.
- Unit tests can be thought of as "living documentation" for your code.
- Unit tests, especially in a test-driven development methodology, are quasi-design documentation.
Why use an automated unit testing framework?
As the name suggests, an automated unit testing framework allows you to automate your unit tests. This lets developers quickly and easily run tests throughout the build process. By not requiring manual action to run unit tests, costs are also minimized. An automated testing framework is also necessary for continuous integration, but that's a topic for another time.
Why write unit tests that do not depend on an actual CRM server?
First, by definition, a unit test is a test of the smallest testable piece of an application. If your unit tests rely on accessing the CRM server, in addition to your code, you are actually testing network connectivity, CRM functionality, the database server, and so on.
Second, trying to maintain an up-to-date CRM environment for unit testing is a pain. It's often hard enough to keep good data in a properly configured QA environment for integration testing and UAT, and it's even harder when you have to deal with multiple developers doing TDD maybe even before your actual QA environment is online.
To accomplish this goal, we need to use a mock object framework. Mock object frameworks allow unit tests to run your code against a mock CRM service so that the tests only validate the code sends the right requests and correctly handles responses without requiring an actual CRM service.
How do you do this with Dynamics CRM?
There are a number of unit testing frameworks for C# code, but my favorite is NUnit. It's relatively popular, well-documented and easy to start using. My example uses NUnit, but you should be able to achieve the same results with alternative tools. The one drawback to NUnit is that it's not integrated with Visual Studio out of the box, but there are a few different ways to integrate them that are beyond the scope of this post.
There are also multiple mock object frameworks for C#, but my current favorite is Moq. I have previously used Rhino Mocks, and while I still think it's an incredibly powerful tool, I have lately found that Moq makes it easier to accomplish my testing goals.
To give you an overview of unit testing Dynamics CRM code with a mock object framework, I've put together a simple example. This example uses .Net 4.0, but it should work with .Net 4.5, too. To follow along, first download both NUnit and Moq from these sources:
After downloading NUnit, install it. This will set up the NUnit assemblies in your GAC, and it will also install the NUnit test runners (GUI and command-line) on your system. You can use NUnit without installing it on your development system, but that will not be covered here.
Once NUnit is installed, create a new class library project in Visual Studio. I like to keep my unit tests in a separate project, but for this example I have them both in the same project and namespace. As soon as the project is created, add references to the following assemblies:
- System.Runtime.Serialization (.Net)
- nunit.framework (.Net)
- Moq (browse to the Moq.dll you downloaded earlier)
- Microsoft.Xrm.Sdk (browse to the .dll in the CRM SDK)
In this example, we have a method called CreateCrmAccount creates a new account record in CRM. This method takes two invocation parameters:
- The name of the account to create
- A reference to an instantiated CRM organization service object
After the method creates the account record, it returns the Id of the newly created record.
public static Guid CreateCrmAccount(string accountName, IOrganizationService service)
{
Entity account = new Entity("account");
account["name"] = accountName;
Guid newId = service.Create(account);
return newId;
}
To verify that this code behaves properly, we need to check two things:
- The "name" attribute of the entity that is passed to CRM is the same as the accountName parameter.
- The method returns the same value as the CRM create method returns.
To do that, we can use the following test method:
[Test]
public void CreateCrmAccount()
{
//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>();
//next - set a name for our fake account record to create
string accountName = "Lucas Demo Company";
//next - create a guid that we want our mock service Create method to return when called
Guid idToReturn = Guid.NewGuid();
//next - create an entity object that will allow us to capture the entity record that is passed to the Create method
Entity actualEntity = new Entity();
//finally - tell our mock service what to do when the Create method is called
serviceMock.Setup(t =>
t.Create(It.IsAny<Entity>())) //when Create is called with any entity as an invocation parameter
.Returns(idToReturn) //return the idToReturn guid
.Callback<Entity>(s => actualEntity = s); //store the Create method invocation parameter for inspection later
//ACT - do the thing(s) we want to test
//call the CreateCrmAccount method like usual, but supply the mock service as an invocation parameter
Guid actualGuid = MockDemo.CreateCrmAccount(accountName, serviceMock.Object);
//ASSERT - verify the results are correct
//verify the entity created inside the CreateCrmAccount method has the name we supplied
Assert.AreEqual(accountName, actualEntity["name"]);
//verify the guid returned by the CreateCrmAccount is the same guid the Create method returns
Assert.AreEqual(idToReturn, actualGuid);
}
This method uses the Arrange - Act - Assert patten in which we first set up everything we need for the test (arrange), then run the code to be tested (act) and finally validate the behavior of the code (assert).
Arrange
In the arrange stage we:
- Create a mock object as a stand-in for the actual CRM organization service.
- Set up some values for the account name and the Id we want the mock service to return when the Create method is called.
- Tell the mock service what value to return when the Create method is called and how to capture a copy of the entity that is passed to the Create method.
Because the Setup call uses "It.IsAny
Act
In the act stage we execute the CreateCrmAccount method.
Assert
In the assert stage we first compare the values of the accountName we passed into the CreateCrmAccount method with the "name" attribute value of the entity it passed to the CRM service Create method. If they are the same, the first Assert.AreEqual call will pass. If not, it will fail.
Next we compare the values of the Id that we told our mock service Create method to return with the value the CreateCrmAccount method returns. As before, if they match, this passes. Otherwise it fails. If either of our test assertions fail, the entire test will fail. If both assertions pass, the entire test passes.
A complete class file is attached as MockDemo.cs (2.96 kb). Add it to your project and build it.
Running the test
Once you have built the .dll, go to your progams menu and load the NUnit GUI (in my case NUnit 2.6.2->NUnit). Then go to File->Open project and browse to the location of the .dll you built and click "Open."
This will load the tests in the assembly (just one in this case). Once you see the project loaded in the left-hand pane, click "Run" at the top of the right-hand pane. This will run the the test, and, if it passes, you should see something that looks like this:
You can also run NUnit from the command line. In addition to displaying test results to the console, you can generate an XML output file that can be consumed by other applications (a continuous integration server, for example.) Here is a screenshot of running our tests via nunit-console.exe with the XML output disabled.
If you want to go further with NUnit and Moq, and you definitely should, here are links to further reading: