Sitecore projects and (un)testable code
Over the last years I’ve been involved with quite some Sitecore projects, some were true greenfield projects where a solution is created from scratch and some involved ‘only’ customizing components or extending the existing platform with new functionality. I enjoy both types of projects since they each have their challenges. I do want to share my concern from what I’ve seen in some of the latter solutions. Some things that all of these projects had in common were:
- Little to no utilization of an ORM, such as Glass Synthesis, CDM (or a well defined self made solution).
- Lack of proper testable code (no dependency injection).
- Lack of unit tests
Of course all these three points are related. If maintainability is important it is vital to any software project that code is written in such a way that it is unit testable. Although this post concerns isolating Sitecore, it could as well be about isolating calls to a custom database or to a logging component.
This post is intended as a practical guide for the ones involved with these ‘difficult’ projects and are strongly in favor of improving the code base in order to improve the testability and maintainability without spending many man months up front to make it happen.
Isolating calls to the Sitecore context
The biggest problem I noticed with some Sitecore solutions is that calls to
Sitecore.Context.Database.GetItem() are all over the place.
The first thing that can be done is to isolate these calls and put this in a custom
ItemProvider class. (Note that Sitecore has its own
ItemProvider class in the Sitecore.Kernel.dll but we’re not touching that one.)
So let’s start with the following very basic interface (
IItemProvider) and implementation (
ItemProvider). It will get more interesting later, I promise.
In every Sitecore solution C# models are used which are based on Sitecore templates. Let’s assume we are dealing with the following
Author object in C#.
Author instances are usually retrieved via a specific provider such as the
AuthorProvider below. (The class name in the gist below is a bit longer because I’ll show another flavor of this provider in a next post).
Notice that the constructor of
AuthorProvider requires an instance of a type that implements
IItemProvider (this is an example of constructor injection). The
GetAuthorItem() method calls the
GetItem() method on the
IItemProvider and this construction enables us to unit test the
AuthorProvider using Sitecore.FakeDb and Moq (or any other mocking framework you prefer).
Unit tests with Sitecore.FakeDb and Moq
Sitecore.FakeDb is a very nice unit testing framework which allows creation and manipulation of Sitecore items in memory.It’s quite easy to get started with. Just install the NuGet package and follow the instructions carefully because some Sitecore & Lucene assemblies and a valid Sitecore license are required. In the example below I only use FakeDb as a source for getting Sitecore items. Moq is a very popular mocking framework. If you don’t know it make sure you read at least the [quickstart[(https://github.com/Moq/moq4/wiki/Quickstart).
Here is the unit test for the
The goal of the unit test is to verify if the
GetAuthor() method of the
AuthorProvider returns an Author object when a Sitecore item Id is passed in as a parameter. When the
AuthorProvider is used in a website context an
ItemProvider is passed into the constructor of the
AuthorProvider and the item is retrieved from the Sitecore context. In the unit test however we don’t want any dependency on the Sitecore context. Therefore an mock is created based on the
IItemProvider interface. We can set-up the
GetItem() method on the mock to return a fake Sitecore item which we will get from Sitecore.FakeDb.
Let’s look at the the unit test in more detail.
- Since FakeDb is an in memory database an instance is created inside a using statement. This ensures that the in memory database is disposed properly after running the unit test.
- In order to create a new
DbItem(from Sitecore.FakeDb) an item Name, Id and Template Id are required. The
GetFakeAuthorDbItemmethod constructs the
DbItemwith fields for the author name and the company.
- Once the
DbItemis created and added to the FakeDb instance we retrieve the Sitecore item (
fakeAuthorItem) from FakeDb.
- Next an
itemProviderMockobject is created based on the
fakeAuthorItemis passed since that is used as the returning item for the mocked
GetItem()method (see the
GetItemProviderMockmethod how that is set-up).
- In the final line of the Arrange section an instance of the
AuthorProvideris created and 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
GetItem()method of the mocked
IItemProvider. A Sitecore item (from FakeDb) is 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 Sitecore item.
Though this example is fairly straightforward it demonstrates how to write testable code when you’re dealing with Sitecore projects. Writing testable code and using a mocking framework in combination with Sitecore.FakeDb in unit tests can be a bit of a learning curve but I consider these as must have skills for any Sitecore developer these days.
In the next post I’ll show a similar approach with an
ItemProvider that uses an
ItemAdapter instead of a regular Sitecore item.
The full source code that is used in this post (and lots more) is on GitHub. Feel free to poke at it and suggest improvements.