May 11, 2011

Unit testing SharePoint 2010 with Moles

The unit testing of SharePoint code has been never easy task to achieve due to the nature and complexity of SharePoint object model and its dependency on the SharePoint server as a platform.
It this article I will share my experience with one of the frameworks which provides a way for unit testing SharePoint 2010 code – Moles.
There’s not much info over the web about how to utilize this framework. So, I guess the best way to get into this is to do what I did – sitting down and spending time trying to figure out how it works. It has its own learning curve to pass and it wasn’t easy to me.
I recommend you to start with these links:

http://research.microsoft.com/en-us/projects/pex/
http://research.microsoft.com/en-us/projects/pex/pexsharepoint.pdf

Before I dive into the beautiful code of moles tests, I will give short introduction what I am trying to unit test. Let’s consider next scenario as business case:
I want to create a custom web part and I want to pass SPListItemCollection collection to method which iterates through it, fetches some of the SPField objects (based on what the consumer code requires) and to generate the header and the content of the web part control.

The source which my code will run against is a custom SPList.



The consumer code is a simple Console application which lists the generated custom columns model.
Below is a screen-shot of the main method of the console application which I created for the sake of the demo.

Within its body it pretty much passes a list of CustomConfigColumns, initializes a result collection of CustomColumnDefintion and prints it out in the console window. In the real-world scenario the result collection might be used for generating HTML.



Since I’d like to keep the dependency of the custom web part control to SharePoint object model low, I created two classes which will help me do this.



The consuming code (Console app in our case) passes CustomConfigColumn list with the names of the SPField objects which must be selected from the field collection (These names correspond to the SPField internal name). In the InitializeCustomColumnDefinition method fields are iterated, selected and as result a list of CustomColumnDefinition objects is returned. After that the custom web control’s code will take care of consuming these custom collections (which is out of the scope of this article).
The screen shot below contains the entire method which I am talking about. There is nothing fancy about it.



Now, let’s focus on how I will unit test the InitializeCustomColumnDefinition method with the Moles framework.


Fist, go to the method definition and from the context menu select “Create Unit Test”:



I named my test project CoreUnitTests. Visual Studio will add all needed reference for you. Once the test project is created I can go to the project’s references, find Microsoft.SharePoint.dll and from right click context menu I can select “Add Moles Assembly”. Just to mention, in order to have this option you must have Pex and Moles framework installed.


As result from this operation 2 new assemblies will be added to the project’s references:
• Microsoft.SharePoint.Moles.dll
• Microsoft.SharePoint.Behaviors.dll

Once you start dealing with Moles, don’t forget to mark your unit test method with HostType attribute if you want to be executed using Moles.



Looking at the InitializeCustomColumnDefinition method i can clearly point out that my code uses next SharePoint objects:

SPListItemCollection
SPField
SPFieldCollection

And I am using next methods which strictly pertain to SharePoint object model:

GetFieldByInternalName
ContainsField
And the next properties: InternalName, Id, Title.

These are the objects which must be moled in order to get my unit tests for this method properly working.

May be the best learning strategy (till you get familiar with the framework) is to start from the main object which in my case is SPListItemCollection, to mole it and to keep moving on after the test execution failures, until you get it working. After that you can think about Behaved types which in my opinion are much more intuitive and focus on the behavior and used SharePoint objects, and properties in the tested methods.
Once you start moving on, Moles will give you hints through its exceptions and will guide you with the methods and properties which are not implemented and referred in your code.


Well, at the end my unit test method looks like on the screenshot below.
In the test method I am creating a list of column definitions, and I am asserting that the number of the returned collection is equal to the input collection count.



Well, in the terms of the SharePoint dependencies which I listed above, i need to find a way to provide moles types replacements for the ListItemCollection, its FieldCollection and to be able somehow to get a SPField by calling ContainsField and GetFieldByInternalName methods. Hence, i need to provide mole substitutions for these methods, objects and properties.
I think that the best approach when utilizing Moles is to focus on what we need moled before we start figuring out how to do it. So our first question when building unit tests based on Moles should be – what SharePoint objects and collections I have, how are they related and what properties I use.
Here is the place to share my opinion that Moles syntax is quite awkward and you really must be familiar with Action, Func lambda expressions and delegates.
Helpful hint might be the naming of the Moles methods.
The suffix of the Moles method is indeed the parameters of the SharePoint function. In addition the intellisense helps you with the parameters and the result types once you start typing the names.

GetFieldByInternalNameString corresponds to

public SPField GetFieldByInternalName(string strName);

As it comes to the properties, their getter has “Get” suffix in Moles object:

sourceCollection.FieldsGet = () => fieldsCollection corresponds to
dataSourceCollection.Fields

As briefly describe next code line
sourceCollection.FieldsGet = () => fieldsCollection as : return SPFieldCollection by passing no arguments (at the end it is property…).

It was a bit tricky to simulate the returning of SPField object by its internal name (string). To achieve this I created a hashtable with the keys of SPFields internal names and values - the moledSPField objects.



At the end i call the method (Act) I am testing, and i make assertions about the result.



Behaved types
Once I was done with this, I thought myself – can I do it using behaved types?
So, I started looking at what Moles framework gives you with its behaved types OB (out-of-the-box).

Behaved types have prefix BSP and the moles types have MSP.
Pex and Moles comes with the source code of the behaved types, so it is worth taking a look on it.

The difference between both approaches is that with when your code accesses a method or property which is not initialized, your will get BehaviorMissingValueException with the behaved types and with Moles you will get MoleNotImplementedException.

Both behaved types and Mole types address the same problems. Still the same methods, properties and objects must be moled and their SP objects detoured during unit test execution.
In my opinion the behaved types are much more straightforward and they focus on exactly what must be tested. Great advantage is the fact that they syntax is easier for understanding and reading.

Below is the screen-shot from the same method, but unit tested with behaved types.


You can easily make the difference with Mole types unit test. I don’t need helper collections like Hashtables. I don’t have the weird names of methods and properties and the lambda expressions, Func, Action and delegates. Instead of them I have “regular every day normal” properties and names.

Still, I use one mole type which is the “top” object in our SharePoint method (I think about it as my entry point in the world of the SharePoint objects which must be moled) – MSPListItemCollection.

Extending Behaved types
What happened with ContainsFieldString and GetFieldByInternalNameString?

I cannot bypass them because my code uses them. Well, with the behaved types you have 2 options – either you get this method OB (as with the properties like InternalName, Id, Title for BSPField) or you should implement them on your own (life is unfair).

When you face such problem during your test, you better stop and take a look in the Behaved Types source code rather than start developing workarounds which most likely may not work (like I did. …).

In my case I created class BSPFieldCollectionExtension which has one method CustomExtension. Within it I simply define what I need and what is not defined in the behaved type which I am using.

this.Holder in my case is MSPFieldCollection.



Once I am done with the extensions I call run my test and see both of them are working.



That is. I am done with the unit testing of my method using Moles.

Now things like code coverage sound more achievable and realistic. You can combine Moles with any other mock frameworks as with your own stubs as well.
I hope this article will be helpful to the people and developers who care about the quality of the code. Any feedback is appreciated.

You can download the code related to the discussed example here.

3 comments:

  1. Nice Post Kaloyan. Very well written and hopefully many can benefit.

    Radi A.

    ReplyDelete
  2. How can we execute unit test on build m/c where SharePoint is not installed?
    Is there any way to do that?

    ReplyDelete
  3. Hi

    @Himanshu. I have a simlar requirement to you, in that I want to run my tests on my laptop without SharePoint. I have imported the SharePoint DLLS and imported Computer\HKEY_LOCAL_MACHINE\Microsoft\Share Tools\Web Server Extension\14 . So I can create my solution and add my web parts. I actually point the startup URL at the my remote SharePoint. I don't think we can do anything but package up the solution in this scenario. I would be great to run the tests.

    The other comment I had was, is there an easy way to read my mock list values from a config file or do we have to hard code them. The screen dump show the column names but not the values I want to test.

    ReplyDelete