As a TDD aficionado I never feel my code complete without writing unit tests. However you may not always be lucky to be working with a framework which has been designed with testability in mind, for e.g. ASP.NET MVC or WF4. CRM 2011’s plug-in programming model is definitely not one that belong to the “testable” category. This weekend I decided to venture out to see what it takes to unit test a a CRM plug-in.
CRM plug-ins are designed to be run inside the CRM server’s execution environment (mostly sandboxed) and often access CRM’s web services to create or retrieve entities. Hence it was clear that we have to use test doubles to make the plug-in run without the CRM environment. I decided to go with Moles framework from MSR (Microsoft Research), which can be downloaded for free. If you are not familiar with test doubles, I strongly recommend you read Martin Fowler’s concise and precise piece on it here or take a look at the xunitpatterns.org website.
Download and install Moles framework from here. Remember to download x86 or x64 versions depending on your VS 2010 version.
The sample CRM plug-in shown in this blog leverages CRM 2011 developer toolkit. Though I strongly recommend it, it is in no way a pre-requisite for understanding how to unit-test a plug-in. Code below shows a very simple plug-in which validates the “telephone1” attribute of the account entity and throws an exception if the phone number is not a valid US phone number.
Add a test project to your CRM 2011 solution. Add a reference to Microsoft.Moles.Framework assembly
Add references to Microsoft.Xrm.Sdk and your plug-in assemblies to the test project.
Let us start writing the unit test for the plug-in. The code in your unit test can be as simple as creating an object of your plug-in class and calling its Execute method.
However Execute method of the IPlugin Interface expects a parameter of type IServiceProvider from the System namespace in mscorlib assembly, a standard interface existed since .NET framework 1.0 days. IServiceProvider is nothing but a container (or dictionary) of services, indexed by the types of the contained services (or its abstract base classes/interfaces). When the plug-in is being executed by the CRM platform, CRM platform is responsible for creating the IServiceProvider object populated with services needed by the plug-in and passing it to the Execute method. In a unit test method it is the responsibility of the author to provide all the dependencies required to run (test) the code being tested. Though you can manually create your own implementation of IServiceProvider and pass it to the Execute method, Moles framework comes handy here.
Right-click the “References” node of your Test project to “Add moles assembly for mscorlib”. This action add a new file to your project “mscorlib.moles”. Compile your test project and you will see that a reference to mscorlib.Moles assembly has been added to your project automatically.
mscorlib.moles assembly contains stubs for various interfaces -including IServiceProvider- from the mscorlib assembly. What this means is you can re-write your boilerplate unit test code as shown below
Take a look at the SIServiceProvider class, which is present in the System.Moles namespace. The name of the class merits some decoding since it will help you understand the standard nomenclature moles framework uses. The class is named SIServiceProvider because it is a “Stub” for “IServiceProvider” interface. Any stub class moles framework creates will be named as per this rule. This stub class, more than implementing the IServiceProvider interface, allows you to substitute methods and properties you are interested in (or your code -being tested-uses).
We have used the stub class for IServiceProvider in the above code, however it is not complete without stubbing the GetService method of IServiceProvider. Below is the temporary stub for the GetService method
when CRM platform executes a plug-in it expects the IServiceProvider container object to contain three objects of type ITracingService, IPluginExecutionContext and IOrganizationServiceFactory. ITracingService is not mandatory if you are not doing any tracing, hence I have omitted the check in the GetService method stub.
As you guessed correctly, I am going to add stubs for IPluginExecutionContext and IOrganizationServiceFactory and they will be named as SIPluginExectuionContext and SIOrganizationServiceFactory. Since these interfaces are present in the Microsoft.Xrm.Sdk assembly, let us add a mole file and create the stub classes. Right-click the Microsoft.Xrm.Sdk reference node and “Add Moles Assembly”.
This will add a new file called Microsoft.Xrm.Sdk.moles to the project. Build your test project, note that a new reference to “Microsoft.Xrm.Sdk.Moles” assembly is added automatically. This assembly contains stubs for interfaces explained earlier. The refactored unit test method using these stub classes should look like
We have successfully stubbed the GetService method of the IServiceProvider interface. Similarly we have to stub out methods and properties of IPluginExecutionContext and IOrganizationServiceFactory.
Following code shows how we can stub IPluginExecutionContext interface.
Line number 2-4 provides your own implementations of various properties because these are used in the constructor of your plug-in as shown below
Lines 5-12 stubs properties as required by your plug-in code, especially InputParameters propery, “Target” indexer and “telephone1” attribute.
It is time to provide a stub implementation for IOrganizationServiceFactory
CRM uses IOrganizationServiceFactory interface’s CreateOrganizationService method to return an implementation of IOrganizationService interface. IOrgnizationService interface as well as CreaeOrganizatinService method are stubbed as shown in the above code sample.
It is time to run your test method. However, if you look at the phone number “12345”, you already know that your code is going to throw a InvalidPluginExecutionException. Add the ExpectedException attribute to your test method so that it knows what exception to expect. Your test method should like
Right-click your test method and run it inside visual studio. Observe the Test Results window to see whether it passed. You can add another test method with a valid US phone number to test the success code path of your plug-in. Alternately you can use Microsoft Pex for parameterizing your input values and create PUTs (Parameterized Unit Tests).