Recap of Part I
This is part two of the “Isolating calls to Sitecore.Context…” series. If you haven’t read the Part I please do so to get the right context (pun intended).
In Part I
the GetItem() method from
ItemProvider returned an actual Sitecore Item. Because of the
IItemProvider interface and Sitecore.FakeDb it is possible to return fake Sitecore items and no dependency to the Sitecore context is required in unit tests.
Although unit testing is now possible there are some (minor) downsides to them due to Sitecore.FakeDb:
- Unit tests still require additional Sitecore assemblies and the Sitecore license file.
- Unit tests look a bit cluttered due to setting up the fake Db and DbItem.
- Unit tests are not very fast to execute.
So lets look at another way of dealing with Sitecore items to get very lean unit tests.
I prefer to use abstractions of Sitecore objects because they make unit testing so much easier. The abstractions act as an adapter. It wraps the Sitecore object and exposes some frequently used properties and methods of that object. The adapter or wrapper pattern in combination with Sitecore is quite common and has been described earlier by several others (e.g. Alistair Deneys and Martina Welander).
So instead of working directly with a Sitecore
Item we can work with an
IItemAdapter interface which is implemented by the
Note that the original Sitecore
Item is accessible through the
Code that should be unit testable should rely only on the other properties the adapter exposes. Code that requires
Item properties which are not exposed directly by the
ItemAdapter (and don’t require unit testing) could use the
Let’s have a look now at the new
IItemProvider interface and
A new method is added called
GetItemAdapter(). When in the web context the
ItemProvider will call it’s own
GetItem() method which will return an actual Sitecore
Item and wrap it in an
ItemAdapter. In a unit test context however
IItemProvider will be mocked and the
GetItemAdapter() method will be set-up to return a fake
ItemAdapter (i.e. not based on a Sitecore
Let’s recall the
AuthorProvider example which was used in part I. Here’s the new
AuthorProvider class where the
GetAuthorItem() method now calls the
GetItemAdapter() method of the
ItemProvider and thus returning an
Unit tests with IItemAdapter and Moq
Here is the unit test for the
GetAuthor() method when the
AuthorProvider works with an
When compared with the unit test in the first post (which used FakeDb) this unit test is slightly more compact and easier to understand. Don’t get me wrong, I really like Sitecore.FakeDb but use it only when you can’t use an adapter.
Let’s look at the unit test in more detail.
- First a new
Idis generated which will be used for the
GetAuthorItemMock()method contructs a mock object (
authorItemMock) based on
IItemAdapterand requires parameters for the Id, author name and company name.
GetItemProviderMock()method constructs a mock (
itemProviderMock) based on
authorItemMockis passed as a parameter since that will be the result of the
GetItemAdapter()method of the mock.
- An instance is created of the
itemProviderMockis passed in the constructor.
GetAuthor()method on the
AuthorProvideris called. Inside this method the
GetAuthorItem()method is called which in turn executes the set-up
GetItemAdapter()method of the mocked
IItemProvider. A mocked
IItemAdapteris returned and mapped to a new
- An assertion is done to check if the Name property of the
Authorobject is equal to the author name field of the mocked
Creating adapters for Sitecore objects can be a relatively quick way to get unit testable code as long as dependency injection principles are used. You are in complete control of the adapter interface. You can start with a very lightweight interface and just expose a couple of properties you need for proper unit testing. Then you can gradually introduce additional properties to the interface as needed.
Next to the Sitecore
Item, other frequently adapted Sitecore objects are
SiteContext. More of that in a later post.
The full source code that belongs to this post (and more) can be found on Github.