Archive

Archive for February, 2012

Unit testing with Sitecore Item

07/02/2012 6 comments
This post is about how to do unit testing  with Sitecore. Off course this is not easily done, because Sitecore requires a “context” to work.  There are a couple of blogs out there , that explains how to setup either a custom test runner, where you can xecute tests through a webpage. or alternative setup an app_config file so you can work with Sitecore without a httpcontext. Links to both examples are given in bottom of this posts.
Both are really good, but I would categorize testing this way more as Integrations tests.  Since they both depend on external resources  such as  Sitecore Database or httpcontext, it would be more fair to  call them Integration tests. Not that you shouldn’t do integration test you SHOULD, but theese are more “heavy to run”. A unit is the smallest testable part of an application, and a unit test should not depend on the external things such as a database, file system or a like, and more they should run fast, and by a click of a button. I’ve recently read the book  Working Effectively with Legacy Code by Michael Feathers, and it got me thinking there got a be an alternative way to do unit testing with Sitecore.
Finally I’ve recently had a discussion with one of collegeues, the discussion was about how you could do unit test with Sitecore and what you would win by doing so. He claimed that test would be more complex then the code it test, and you wouldn’t gain anything from such tests. This inspired me to do some extensive testing. To prove him wrong I found some code he wrote for a Sitecore solution see below, the code is taken from a navigation component and finds the navigation title fom a item,  navigation title is only shown if the item if   “show in menu”  checkboxfield is checked. Keep in mind the that I picked this example because it is simple.
public class NavigationTitleFactory
 {
 public static string Create(Item item)
 {
 bool showInNavigation = item.GetCheckBoxField(new ID(Constants.Fields.Navigation.Show));

if (!showInNavigation)
 {
 return string.Empty;
 }

string navigationTitle = item.GetStringField(new ID(Constants.Fields.Navigation.Title));

 if (string.IsNullOrEmpty(navigationTitle))
 navigationTitle = item.Name;

 return navigationTitle;
 }
 }

So the first problem that you meet is that a Sitecore Item is difficult  to mock or stub out for test, it would have been really nice if it had  inherit from a interface or had some virtual methods you could override in  a test implementation of with a mocking framwork.
But it turns out you can actually instantiate a Sitecore Item all  you have to do is passing in a lot of nulls “W.E.W.L.C by  M.Feather page 121” see the result below.
public class TestItem : Item
 {
 public TestItem(FieldList fieldList,string itemName="dummy")
 : base(new ID(new Guid()), new ItemData(new ItemDefinition(new ID(new Guid()), itemName, new ID(new Guid()), new ID(new Guid())), Language.Invariant, new Sitecore.Data.Version(1), fieldList), new Database("web"))
 {

}
 }
 With this test item, writing test should now become a lot easier. The itemName is optional because offen you dont care about the name of your item, but for this show case we do.
If you like ,you could pass in addiational arguments to the test item like database name, language, or version. and would still work. The fieldlist arguments should contain the list of field values you gonna use in your test, the item fields can only be referenced with Sitecore id’s as you will see in the example below. Now back to code challenge from my colleague . I want to test the logic for the  navigation title factory liseted above. Here are the test cases.

  1. An Item with show in navigation not checked – it should return an empty string.
  2. An item with show in navigation checked but no navigation titel should return the Name of the Item.
  3. An item with show in navigation checked and with a navigation title, it should return the value of the navigation title.

The test code i listed below should give 100% test coverage of the code.

[TestFixture]
 public class NavigationTitleFactoryTest
 {
 [Test]
 public void CreateItemWithShowInMenuFalseShouldReturnEmptyString()
 {
 FieldList stubFieldList = new FieldList();
 stubFieldList.Add(new ID(Constants.Fields.Navigation.Show),"0");
 stubFieldList.Add(new ID(Constants.Fields.Navigation.Title),"NavigationTitle");
 Item stub = new TestItem(stubFieldList);

 string navigationTitle = NavigationTitleFactory.Create(stub);

 Assert.IsNullOrEmpty(navigationTitle);
 }

[Test]
 public void CreateItemWithShowInMenuTrueNoNavigationTitleShouldReturnItemName()
 {
 FieldList stubFieldList = new FieldList();
 stubFieldList.Add(new ID(Constants.Fields.Navigation.Show), "1");
 stubFieldList.Add(new ID(Constants.Fields.Navigation.Title), "");
 Item stub = new TestItem(stubFieldList,"myItemName");
 string navigationTitle = NavigationTitleFactory.Create(stub);

Assert.AreEqual("myItemName", navigationTitle);
 }

[Test]
 public void CreateItemWithShowInMenuTrueShouldReturnItemNavigationTitle()
 {
 FieldList stubFieldList = new FieldList();
 stubFieldList.Add(new ID(Constants.Fields.Navigation.Show), "1");
 stubFieldList.Add(new ID(Constants.Fields.Navigation.Title), "NavigationTitle");
 Item stub = new TestItem(stubFieldList);
 string navigationTitle = NavigationTitleFactory.Create(stub);

Assert.AreEqual("NavigationTitle",navigationTitle);
 }
 }
This is  a simple example of how to do some unit testing with sitecore. Off course there are some problems with this approach you can’t access more complex attributes and functions of the Sitecore item, for example Children, path, and  functions in general that needs to communicate with Sitecore. But you can test more complex logic that are based in field values alone.
But what did we win with these tests, lets look at it this way. First of i saved time going in to Sitecore creating items setting field values and publishing, agreed it doesn’t take that long but with test like this i can repeat verification of my code by a click of a button. Also Should the Navigation Title Factory really care about which items it get from sitecore ie  the database  pr which context it runs in ? And it would even be pretty simple to add fallback from navigation title to another field value instead of item name and test this new functionality and ensure that the old functionality still works as excepted. And this is precisely what you want from a unit tests.With a unit test you it should be easy to change/modify ,refactor or add functionality to existing code and easily verify that you didn’t brake anything.
I wouldn’t test pure datacontainers, Items that only holds data to be shown. I would prefer to test these with integrationtest.

Allstair Deneys blog on unit testing with
Sitecore using a custom test runnner can be found here.How to change config files to work with Sitecore without a “context” here. By Mike Edwards