Sitecore 8.2: Mock your Items

Leave a comment

September 8, 2016 by Alistair Deneys

Back in January I wrote an article about how to Roll your own Sitecore Item, which showed how to create an in-memory Sitecore item for use in testing. The technique was quite limited and still required some small config entry to allow everything to work (to create a Database instance).

Well, Sitecore 8.2 has finally made a reality what so many of us have prayed for, for so long. We can finally mock Items and Databases with Sitecore 8.2. Though, it’s probably not quite as simple as you might hope. Keep in mind, items aren’t DTOs to be newed up here and there. An Item is instantiated within the data layer, so it doesn’t have a parameterless constructor, which is required by many mocking frameworks. To mock an Item, we still need the arguments for it’s constructor.

So here’s the minimal code to mock an Item. Note how we can mock the DB now. BTW, I’m using Moq as my mocking framework.

var id = ID.NewID;
var templateId = ID.NewID;
var def = new ItemDefinition(id, "fake", templateId, ID.Null);
var data = new ItemData(def, Language.Parse("en"), 
  new Sitecore.Data.Version(1), new FieldList());

var db = Mock.Of<Database>();
var itemMock = new Mock<Item>(id, data, db);
var item = itemMock.Object;

That item there is not a real Item, it’s a mocked item! And because we’re mocking, and all the Item members are now virtual, we can use setups to configure member behavior. This allows us to fill in gaps from the previous technique like accessing fields by name.

itemMock.SetupGet(x => x["field"]).Returns("value");
var item = itemMock.Object;

var value = item["field"];
Assert.That(value, Is.EqualTo("value"));

Or how about accessing the content tree? We’ll need a few mock items for this setup, so let’s wrap up the above code in a utility method we can call to create a mocked item named CreateMockedItem(). Now we can setup the Children property to return a list of mocked items:

var item = itemMock.Object;

var children = new ChildList(item, new[]
{
    CreateMockedItem(),
    CreateMockedItem()
});

itemMock.SetupGet(x => x.Children).Returns(children);

var childItem = item.Children[1];
Assert.That(childItem, Is.Not.Null);

How about creating items? We can setup the Add() method to return a mocked item as well:

var addedItem = CreateMockedItem();
itemMock.Setup(x => x.Add(It.IsAny<string>(), It.IsAny<TemplateID>()))
  .Returns(addedItem);
var item = itemMock.Object;

var newItem = item.Add("my new item", new TemplateID(ID.NewID));
Assert.That(newItem, Is.Not.Null);

OK, lot’s of good stuff there. But not all scenarios only use direct members of the item. Some scenarios we’ll want to run will need to mock objects which the item exposes. Let’s access the name of the item’s template. The Template property of the Item gives us access to the template and the Name property hangs off that (Let’s ignore the TemplateName property of Item for a second). We’ll need to mock the template, and then mock Template access on the Item to return the mocked template:

var fakeTemplateItem = CreateMockedItem();
var templateMock = new Mock<TemplateItem>(fakeTemplateItem);
templateMock.SetupGet(x => x.Name).Returns("fake template name");
var template = templateMock.Object;

itemMock.SetupGet(x => x.Template).Returns(template);

var item = itemMock.Object;
var templateName = item.Template.Name;

Assert.That(templateName, Is.EqualTo("fake template name"));

So yeah, we’ve got the ability to mock an Item. But given how much code we need to write to fulfill many scenarios, perhaps mocking isn’t always what we’re after. FakeDB still makes plenty of scenarios much easier to implement with less code than when we mock everything ourselves.

So add Item mocking to your toolbox. Just remember to use the right tool for the job.

Advertisements

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s

The views expressed on this blog are solely my own and do not necessarily reflect the views of my employer.
%d bloggers like this: