Updated 5 May 2012 Also see my follow up post, this corrects some of the information of faking SharePoint
I have done posts in the past about how you can use Typemock Isolator to fake out SharePoint to speed design and testing. The reason you need special tooling, beyond standard mocking frameworks like Rhino or MOQ, is that SharePoint has many sealed private classes with no public constructors. So in the past you only had two options: Typemock Isolator and Moles from Microsoft research.
With the release of the Visual Studio 11 beta we now have a means to fake out ‘non mockable classes’ (shim) classes that is shipped in the box. This tooling I understand has it roots in Moles, but is all new. So with the advent of fakes in VS11 you have to ask ‘do I still need Typemock isolator?”
To answer this question I have tried to perform the same basic mocking exercise as I did in my previous posts. Create a fake SharePoint list and make sure I can access the faked out content in test asserts.
In Typemock Isolator
To perform the test in Isolator, assuming Isolated is installed on your PC, is add a reference to Typemock.dll and Typemock.ArrangeActAssert.dll and use the following code
\[TestMethod\] public void FakeSharePointWithIsolator() { // Arrange // build the dataset var fakeItemList = new List<SPListItem\>(); for (int i = 0; i < 3; i++) { var fakeItem = Isolate.Fake.Instance<SPListItem\>(); Isolate.WhenCalled(() => fakeItem.Title).WillReturn(String.Format("The Title {0}", i)); Isolate.WhenCalled(() => fakeItem\["Email"\]).WillReturn(String.Format("email{0}@fake.url", i)); fakeItemList.Add(fakeItem); } // fake the SPWeb and attach the data var fakeWeb = Isolate.Fake.Instance<SPWeb\>(); Isolate.WhenCalled(() => fakeWeb.Url).WillReturn("http://fake.url"); Isolate.WhenCalled(() => fakeWeb.Lists\["fakelistname"\].Items).WillReturnCollectionValuesOf(fakeItemList); // act // not actually doing an operation // assert Assert.AreEqual("http://fake.url", fakeWeb.Url); Assert.AreEqual(3, fakeWeb.Lists\["fakelistname"\].Items.Count); Assert.AreEqual("The Title 0", fakeWeb.Lists\["fakelistname"\].Items\[0\].Title); Assert.AreEqual("email0@fake.url", fakeWeb.Lists\["fakelistname"\].Items\[0\]\["Email"\]); Assert.AreEqual("The Title 1", fakeWeb.Lists\["fakelistname"\].Items\[1\].Title); Assert.AreEqual("email1@fake.url", fakeWeb.Lists\["fakelistname"\].Items\[1\]\["Email"\]); }
Using Microsoft Faking
Adding the Fake
The process to added a fake in VS11 is to right click on an assembly reference (in our case Microsoft.SharePoint) and select the ‘add fake assembly’ option.
You should see a Fake reference created and an entry in the fakes folder
Gotcha Warning ‘Can’t generate the fake reference’ – When I tried to generate a fake for the Microsoft.SharePoint assembly within a classlibrary project that had a reference to only the Microsoft.Sharepoint assembly (and the default assemblies references added for any classlibrary project) the entry is made in the Fakes folder but no .Fakes assembly is created. After a delay (30 seconds?) you see an error message in the Visual Studio output window. This tells you a reference cannot be resolved, if you delete the entry in the Fakes folder, add the missing reference listed in the output windows and repeat the process you get the same problem but another assembly is named as missing, add this and repeat this process. Eventually the .Fakes assembly is created.
In the case of this SharePoint sample I had to manually add Microsoft.SharePoint.Dsp, Microsoft.SharePoint.Library, Microsoft.SharePoint.Search, System.Web, System.Web.ApplicationServices. Remember these entries are ONLY required to allow the fake creation/registration, they are not needed for your assembly to work in production or for Typemock. [5 May 2012 See my follow up post]
You should now have a generated assembly that you can use to create your fakes shims
Writing the fakes logic
The logic to create the fake behaviour is as follows. Now there might be easier ways to do this, but this does work and is reasonably readable
\[TestMethod\] public void FakeSharePointWithShims()
{ using (ShimsContext.Create()) // required to tidy up the shim system { // arrange var fakeWebShim = new Microsoft.SharePoint.Fakes.ShimSPWeb()
{
UrlGet = () => [http://fake.url](http://fake.url),
ListsGet = () => new Microsoft.SharePoint.Fakes.ShimSPListCollection()
{
ItemGetString = (listname) => new Microsoft.SharePoint.Fakes.ShimSPList()
{
ItemsGet = () => new Microsoft.SharePoint.Fakes.ShimSPListItemCollection()
{ // we have to fake the count, as we not returning a list SPListCollection CountGet = () => 3,
ItemGetInt32 = (index) => new Microsoft.SharePoint.Fakes.ShimSPListItem()
{
TitleGet = () => string.Format("The Title {0}", index),
ItemGetString = (fieldname) => string.Format("email{0}@fake.url", index) // note we don't check the field name }
}
}
}
}; // act
// not actually doing an operation
// assert var fakeWeb = fakeWebShim.Instance; Assert.AreEqual("http://fake.url", fakeWeb.Url); Assert.AreEqual(3, fakeWeb.Lists\["fakelistname"\].Items.Count); Assert.AreEqual("The Title 0", fakeWeb.Lists\["fakelistname"\].Items\[0\].Title); Assert.AreEqual("email0@fake.url", fakeWeb.Lists\["fakelistname"\].Items\[0\]\["Email"\]); Assert.AreEqual("The Title 1", fakeWeb.Lists\["fakelistname"\].Items\[1\].Title); Assert.AreEqual("email1@fake.url", fakeWeb.Lists\["fakelistname"\].Items\[1\]\["Email"\]); }
Comments
Which which do you find the most readable?
I guess it is down to familiarity really. You can see I end up using the same test asserts, so the logic I am testing is the same.
I do think the Typemock remains the easier to use. There is the gotcha I found with having to add extra references to allow the fakes to be create in the VS11 faking system, and just the simple fact of having to manually create the fakes in Visual Studio, as opposed to it just being handled behind the scenes by Typemock. There is also the issue of having to refer to the ShimSPWeb class and using the .Instance property as opposed to just using the SPWeb in Typemock.
The down side of Typemock is the cost and the need to have it installed on any development and build machines, neither an issue for the Microsoft fake system, the tests just being standard .NET code that can be wrappered in any unit testing fame work (and remember VS11’s Unit Test Explorer and TFS11 build allow you to use any testing framework not just MStest.
So which tool am I going to use? I think for now I will be staying with Typemock, but the VS11 faking system is well worth keeping an eye on.
[Updates 24 March 12 - More comments added in a second post]