At the Dallas 365 Saturday event this past March, I attended the Unit Testing for Dynamics 365 Workshop by Jordi Montaña Vázquez. His Fake Xrm Easy unit testing framework provides a collection of pre-built mock objects for Dynamics CRM SDK objects that greatly simplifies Dynamics CRM unit tests. Jordi’s framework had been on my radar for some time but this was a perfect opportunity to talk with the man himself about usage and best practices. The session was excellent and it became obvious that his framework is a must have for unit testing Dynamics CRM code.
I had hoped that Jordi could attend and speak at our 365 Saturday DC event coming up on September 21st and 22nd but since he can’t make it, he’s allowed me to present in his place. As part of my prep for the session, I am writing a few posts about unit testing and the Fake Xrm Easy framework. Thanks to Jordi for providing feedback and some insight on this post before publishing!
Unit Testing Dynamics CRM
Unit testing can be a contentious topic for application developers and can often lead to some heated arguments. In my experience, these arguments usually do not revolve around whether to actually write unit tests, but rather which framework is best and the amount of coverage required for your project. Fortunately, developers have options for unit testing frameworks, each with their own strengths and weaknesses.
Prior to working within Dynamics CRM, I built unit tests for a custom application. Our framework of choice was NUnit and then later, Visual Studio Test Suite. We could quickly set up a database connection, create some test/demo data, execute our tests, and then clean things up once our unit test list completes. Writing unit tests within the context of an existing platform requires a different approach and can further complicate a developer’s job. For example, the base Execute method for a Dynamics CRM plugin requires the plugin execution context and a connection to an existing CRM instance in order to properly run. How does a Dynamics CRM developer provide this context required by a Plugin when building out unit tests?
This issue leads us to testing frameworks that allow us to substitute proxy objects in place of our external dependencies, often referred to as mock objects. So when writing our unit tests for a plugin Execute method, I can pass a mock object instead of a real instance of the execution context. One very popular mock testing framework for Dynamics CRM is Microsoft Fakes. In the Isolate code under test with Microsoft Fakes, the first few lines provide a nice explanation:
Microsoft Fakes helps you isolate the code you are testing by replacing other parts of the application with stubs or shims. These are small pieces of code that are under the control of your tests. By isolating your code for testing, you know that if the test fails, the cause is there and not somewhere else. Stubs and shims also let you test your code even if other parts of your application are not working yet.
The article provides some nice examples and explanations, but it’s still a broad topic because unit testing is universal for developers across many platforms. An article that speaks more directly to unit testing Dynamics CRM is by Ben Hosking: Information to Get started with Unit Testing with Microsoft Fakes and Microsoft Dynamics CRM 2013. This is an excellent breakdown of how to get started with unit testing your Dynamics CRM code with Microsoft Fakes.
So now we have a options unit testing framework such as Microsoft Fakes that allow us to write complex unit test code for our Dynamics CRM code. But even with these powerful unit testing frameworks, we face challenges that add time to creating and maintaining our unit tests.
Unit testing means work
The big challenge is that many unit testing frameworks are complicated to learn and setup and then maintain throughout your project life-cycle. I realize that sounds like my complaining about extra work when writing code and making excuses for why unit test coverage is lacking. But when we have a large project with complex integrations, Plugins, Workflow activities, it becomes a challenge to add another large block work while still trying to deliver for your customer in a timely fashion. By finding the right tools that streamline our development process, we can minimize the overhead of building unit tests. Choosing the right tool for your situation can actually improve productivity in some ways, as I think we will see later in the post!
As a simple example, let’s assume the Auto numbering for a Case does not meet our requirements. So we could write some code that generates a Case identifier based on a few fields, such as a Case date field, Customer name, an incremental sequence number persisted on another entity. We could then create a single method called GetCaseIdentifer that takes the Case entity and the OrganizationService as parameters:
public string GetCaseIdentifer(Incident Case, IOrganizationSerice service);
For proper unit test code coverage of this method, I can think of a few unit tests:
- Happy Path – a simple test where all values are present and in the correct format for a new Case
- Missing field values – a new test for each field value, checking for null
- Duplicates – write a unit test that handles duplicate Case Identifiers
- Updates – unit tests that update an existing Case Identifier
These are pretty straightforward test scenarios for the Case identifier logic and I am sure we could identify more. But we see that with our single method for generating a Case identifier immediately means several unit test. And with each unit test above, we need to provide the mock OrganizationService object for the required parameter for each unit test. This might not sound like a problem but once we look at the mock object code, it’s clear that things get complex fairly quickly.
Jordi already provided an excellent post with examples comparing Fake Xrm Easy to other frameworks, specifically the popular Moq testing framework: Fake XRM Easy versus other testing frameworks. In his first example, we can see the code required to set up a mock OrganizationService instance. This is the full code snippet from section #1’s Moq sample:
var entities = new List<Entity>();
Guid id = Guid.NewGuid();
//Expectation: Simulates a create message returning a new Guid and adding the entity to the list
var mock = new Mock<IOrganizationService>();
mock.Setup(service => service.Create(It.IsAny<Entity>()))
.Callback((Entity e) => {
e.Id = id;
entities.Add(e); })
.Returns((Entity e) => id);
//Returns an instance using the expectations above
var service = mock.Object;
//Executes the sut (system under test, a single create as an example)
var guid = service.Create(new Account() {Name = "Test"});
Assert.Equal(1, entities.Count);
That is a decent amount of code to simply set up the mock OrganizationService instance before you even make the service.Create() call. And this Create override method is fairly specific in its behavior: assign the Id of the entity and add it to the collection for the Assert line below. That’s all it really does for this one unit test.
The first line of that section in Jordi’s post stands out:
When you work with .NET mocking frameworks, you need some boilerplate code just to mock the web service calls. That’s done for every call, every single time: it’s a repetitive, tedious task.
So, for each unit test in our Customer identifier example, we need to essentially replicate the mock OrganizationService instance set up code. Now expand that to a real project and we start to see why unit testing can be so complicated for Dynamics CRM projects!
Let’s make things easier
This is where Fake Xrm Easy comes in and really shines. The counter example to the snippet above, we can see our code can be simplified. Below is the Fake Xrm Easy code snippet minus the comments:
var context = new XrmFakedContext();
var service = context.GetOrganizationService();
var guid = service.Create(new Account() {Name = "Test"});
var accounts = context.CreateQuery<Account>().ToList();
Assert.Equal(1, accounts.Count);
This code performs the same work as the previous example with only a few lines of code. But fewer lines of code is not the only benefit we get using the XrmFakedContext. This new context object provides an in-memory version of an OrganizationService context and all of the methods provided by the IOrganizationService interface. For example, in Visual Studio I can see all of the methods as if I were working with an actual OrganizationService instance:
With the Intellisense, we see all the familiar IOrganizationService interface methods and they return the standard objects we expect from the Dynamics SDK. The Fake Xrm Easy framework will also initialize and add the new Account object to and internal collection for later evaluation. When the new Account object is initialized, it will include system attribute values such as CreatedOn and CreatedBy as if this was initialized by a real OrganizationService context. Because the context persisted in memory, we have no round trips to a server so our tests will execute extremely fast.
Additional mock objects
The OrganizationService is important for any unit tests that interact with Dynamics CRM directly. But a likely more common challenge comes with testing Plugins and Workflows. Fortunately, and not surprisingly, XrmFakedContext includes additional mock objects and methods that allow us to test Plugins and Workflows.
Executing a Plugin with the XrmFakedContext is relatively simple. The context object itself provides a method called ExecutePluginWithTarget. This is perfect when testing a Plugin that only acts against the Target within the Plugin context. For example, if we moved our Case identifier scenario into a Plugin named Incident_PostCreate, we could test it with the following snippet:
var ctx = new XrmFakedContext();
var target = new Entity("incident") { Id = Guid.NewGuid() };
var plugin = ctx.ExecutePluginWithTarget<Incident_PostCreate>(target);
The ExecutePluginWithTarget method includes a few overrides that allow you to pass in the Message name, such as Create, and the Plugin stage. So, we have 3 lines of code that allow us to quickly test a plugin.
Additional methods provide more granular control over Plugins requiring Input/Output Parameters, and Pre/Post Entity Images. The ExecutePluginWith method offers options for these varying combinations and has become the default method for developers testing plugins:
var ctx = new XrmFakedContext();
var service = ctx.GetOrganizationService();
// set up the Target for the plugin context
var target = new Entity("incident") { Id = Guid.NewGuid() };
var inputs = new ParameterCollection { new KeyValuePair<string, object>("Target", target) };
// get a new plugin context object
var plugContext = ctx.GetDefaultPluginContext();
plugContext.InputParameters = inputs;
// test the Execute method!
ctx.ExecutePluginWith<Incident_PostCreate>(plugContext);
Once your Plugin execution is complete, you can either evaluate the Target for success or failure using standard Asserts. In the Case identifier example, we could check the value of our expected Case identifier for success. Was it null? Was it in the correct format? Did it throw an expected exception?
Testing our Workflow Code Activities is very similar to testing Plugins. The XrmFakedContext provides the ExecuteCodeActivity method which will take a Dictionary of inputs as a parameter and returns a Dictionary result. The result Dictionary can be used to evaluate whether your unit test was successful or not. For example, say we decided to move our Case identifier code into a Custom Activity that returns a new identifier through an Out Parameter named CaseIdentifier, one unit test might look similar to this:
var ctx = new XrmFakedContext();
var service = ctx.GetFakedOrganizationService();
var inputs = new Dictionary<string, object>() {
{ "Case", new EntityReference("incident", Guid.NewGuid()) }
};
var results = ctx.ExecuteCodeActivity<WorkflowSample>(inputs);
Assert.True(((string)results["CaseIdentifier"]).Equals("New Case ID"));
In the example above, we create a new Case instance as the Input Parameter. When we execute the Code Activity with ExecuteCodeActivity, the Output Parameter value is accessible through the return Dictionary object. So in a few lines of code, I was able to test my Code Activity without the time spent creating a Workflow, deploying, and then executing some test scenarios.
A time saver, too
It’s pretty clear that unit test add time to our schedule because for each bit of code we write, we will need to write more code to test it. But the Fake Xrm Easy framework can actually save time Dynamics CRM development. When creating the examples for this post, I created custom Code Activity in Visual Studio with a new Unit Test project and begin debugging without ever touching a Dynamics CRM instance. Compare this to the typical method of debugging. With Dynamics 365 On Premise, you would need to compile and deploy your code, set up a workflow to invoke the Code Activity, attach to the process, then kick off the trigger for the workflow. In the Dynamics 365 Online, you would have the extra set up of the Plugin Profiler to attach Visual Studio for debugging. With each change to our code, we would need to rebuild, redeploy, and begin debugging again. Using this framework, I made a change and simply reran my unit tests to debug.
It’s also worth noting that for all of our unit tests, we can also assert success or failure by evaluating the context collection of entities. For example, if I build a Plugin that creates a new record in CRM, we can query the context for those newly created objects. Looking at the Plugin example on the getting started section, we see the following few lines of code:
//The plugin creates a followup activity, check that that one exists
var tasks = (from t in
fakedContext.CreateQuery<Task>()
select t).ToList();
The Plugin sample creates a new Task using the XrmFakedContext service instance passed through the mock execution context, so any call that the Plugin makes to this service instance is persisted in-memory. This means we can query our mock context for new records as if we were working against a Dynamics CRM instance.
The examples in this post are pretty simple but I was able to test all of my code locally without the time to set up and deploy and redeploy to Dynamics CRM. This as a significant time saver when beginning development or simply testing a scenario when debugging an issue. This is one of the most powerful features of the Fake Xrm Easy framework.
Summing up
With the Fake Xrm Easy framework, we can quickly set up self-contained unit tests without the need to mock our own Dynamics CRM objects. We now have the ability to test service integration code, Plugins, and custom Workflow Code Activities. For developers, this means faster unit test creation and ease in ongoing maintenance. We also no longer have the overhead of creating separate Dynamics instances against which our unit tests might run. This is a huge time saver as they likely require re-initialization and cleanup each time we run a bank of unit test.
As I continue to prepare for my presentation, I’ll be working out some more detailed examples that I want to discuss with attendees. The Fake Xrm Easy framework include several additional capabilities that I didn’t cover in this post, such as a built in query engine for the in-memory context. I’ll be sure to post the here and would definitely welcome feedback. What are some challenges you’ve faced with unit testing your Dynamics CRM projects?
Personally, I prefer integration tests in a CRM environment, since the real complexity comes from plugins and workflows triggering each other. The testing framework XrmMockup, https://github.com/delegateas/XrmMockup/, allows you to test from a higher level. For example, your test sends a CreateRequest to XrmMockup, then XrmMockup executes all applicable plugins and workflows automatically.
This is done locally, in memory, which means you in reality test against your real CRM system, without touching the online system.
Hi Magnus
Thanks for the comment… that sounds pretty cool and I will need to try it out!
The Fake Xrm Easy framework offers a method for triggering other plugins. You can register a plugin at the time you run other tests and they should simulate the Plugin execution pipeline. For example, this is a really simple example of a plugin that does nothing but fill in the last name when an Update occurs:
// turn on pipeline simulation(“Update”,
ctx.UsePipelineSimulation = true;
// register the plugin step
ctx.RegisterPluginStep
FakeXrmEasy.ProcessingStepStage.Preoperation,
FakeXrmEasy.ProcessingStepMode.Synchronous, 1, new string[] {“firstname”});
// create a new contact record in your context, retrieve it from the internal collection
var contact = new Entity(“contact”);
var service = ctx.GetOrganizationService();
var contactId = service.Create(contact);
contact = service.Retrieve(“contact”, contactId, new ColumnSet());
// update the first name, should trigger the registered plugin
contact.Attributes[“firstname”] = “Jim”;
service.Update(contact);
// now verify that the pre update plugin updated the Target
Assert.AreEqual(“Novak”, contact.Attributes[“lastname”].ToString());
Jim
Cool, did not know it was possible in FakeXrm. Can you also register workflows to that pipeline?
And is it possible to fetch the registrations automatically? Seems annoying to have duplicate registrations, they might become outdated, XrmMockup fetches them from CRM or your code, such that they’re always up to date.
Good question. I will do some digging and get back to you!
jim
I’ll see you in DC James, where I’ll be presenting on the XrmUnitTest framework! Fun fun!
Hey Daryl,
Awesome… looking forward to the conference and glad you could attend!
Jim
Hi,
I am also using fakeXRM easy for unit test. At one point I was struggling, is to check and compare the text of option set in update plugin.
Any idea how we can create/initialize an attribute of type option set with value and text. I could do with value but could not set text (which I wanted for some scenario).