Swapping Collections with Typemock Isolator

One of the new features in Isolator 5.1.1 is fixing an old issue. Let’s say I have a collection I’d like to fake. I use the following:

MyCollection coll= Isolate.Fake.Instance<MyCollection>();

What happens when I try to do the following?

foreach (item in coll)
{
Console.WriteLine(item.Price);
}

The foreach statement, actually calls the collection’s method GetEnumerator, which returns an IEnumerator implementer. Makes sense, but what does it really return? It’s an enumerator that “knows” how to enumerate its parent. But what happens for a fake collection? Even if it does return something, the object returned is usually a fake also. It doesn’t have any knowledge about the fake parent, and if it calls on some of the parent’s methods we’ll probably come to a screeching exception halt.

But let’s get back to what I want to solve: I want a collection, that although it’s a fake, still acting like a collection. That means for a for-each, I want to return my custom values. And not crash, which is also a reasonable requirement.

Isolate.WhenCalled(() => coll).WillReturnCollectionValuesOf(new List<RealLogger>{item1,item2});

The syntax requires a bit more attention than usual. Notice that the WhenCalled Lambda expression contains coll, which is different than the usual method call. With this syntax (which is equivalent to the property get syntax), we identify a collection that we plan to enumerate on. The WillReturnCollectionValuesOf specifies another collection, that will be enumerated when the foreach is invoked on our collection. So in this case, when the foreach clause above runs, it will return two items: item1 and item2.

Let’s take a look at a more advanced case, where a collection is used inside another collection. Here’s some SharePoint code we’d like to test:

public void DeepIterationWithForEach()
{
SPSite site = new SPSite("http://sharepoint.typemock.com");
using (SPWeb web = site.OpenWeb())
{
foreach (SPList list in web.Lists)
{
foreach (SPItem item in list.Items)
{
item.Update();
}
}
}
}

Let’s concentrate on the foreach-inside-foreach. SPLists contain SPItems. Here is some test code:

[TestMethod]
public void SwappingSharepointCollections_IterateOverCollectionWithForEach_SwappedIteratorsAreUsed()
{
SPSite fakeSite = Isolate.Fake.Instance<SPSite>(Members.ReturnRecursiveFakes);
Isolate.Swap.NextInstance<SPSite>().With(fakeSite);

Isolate.WhenCalled(() => fakeSite.OpenWeb().Lists[2].Items).WillReturnCollectionValuesOf(new List<SPItem>
{
Isolate.Fake.Instance<SPItem>(),
Isolate.Fake.Instance<SPItem>(),
Isolate.Fake.Instance<SPItem>()
});
SharepointUsingClass target = new SharepointUsingClass();
target.DeepIterationWithForEach();

var fakeItemList = fakeSite.OpenWeb().Lists[2].Items;
foreach (SPItem item in fakeItemList)
{
Isolate.Verify.WasCalledWithAnyArguments(() => item.Update());
}
}

After faking SPSite recursively, let’s take a look at the WhenCalled clause. It focuses on fakeSite.OpenWeb().Lists[2].Items, which is the third list in the external collection. In the spirit of “fake everything that you don’t care about“, we don’t care about the first or second list. We care about the 3rd one, and this is where we specify what items we return in the collection. We use the WillReturnCollectionValuesOf to specify the items we want, in our case, a List of fake SPItems. Apart from foreach-ing inside the target method, we also enumerate over the list in the test to Verify the calls.

So now you can fake collections easily with Isolator. If you’d go a step further we’ll get to duck-typing. But that’s another post.

Note that you can click the link to learn more on using SharePoint or to learn how to unit test a SharePoint application

Technorati Tags:

Microsoft , Microsoft sharepoint , Unit Testing SharePoint, mock, Software Testing, Swapping Collections

TOP