codeflood logo

Unit Test Test Data

One of the most valuable lessons I learnt whilst doing unit testing with Sitecore projects was to ensure my tests were robust and repeatable. I went through a lot of pain in my earlier projects with brittle tests that as soon as one test failed, all the others would start failing. All this instability was to do with the test data I was using. I had a small amount of test content that I assumed was always in the state I expected. The issue was that if one test failed and didn’t return the data back to the initial state, the other tests would all start failing.

Your unit tests will be much more robust if they create the data they expect at the start of the test then destroy that data at the end. This helps isolate your tests from each other and ensures more repeatable and robust tests. It’s probably naive to assume the content in your CMS right now is exactly as you expect it if it’s been sitting there for a while. There’s nothing like a rogue test trashing your content to really drive home how unstable this approach is.

Depending on what the code under test does will depend on the scope of your test data. For code that only reads the data we can just create the data at the start of our TestFixture and destroy the data at the end. If you had code that updated content or needed to test many different content structures then you would probably need to create the test data at the start of the individual test itself and destroy it at the end.

Recently when I added unit testing to the EviBlog (WeBlog) shared source module I decided to try out a new idea for creating my test content. Prior to this idea I would just use the Sitecore API to create my content.

The Sitecore API for creating content is very straight forward. Grab your template, find the location in the content tree where you want your new content to be created then add the new item below the existing. Then you can set the fields of the item. All in all I think it’s quite clean code and I really don’t know how you would make it any simpler.

var template = Sitecore.Context.Database.GetTemplate(“mytemplate”); 
var parent = Sitecore.Context.Database.GetItem(/sitecore/content/home”); 
var item = parent.Add(“myitem”, template); 

item.Editing.BeginEdit(); 
item[“field”] =value; 
item.Editing.EndEdit();

But once you start adding lots of items for a more complex content structure you very quickly start drowning in code. Just one quick point on structuring the code of your TestFixture. Tests are code just like your program. They also benefit from good coding practises including the use of methods to reduce the amount of repeated code. If you find your self commonly copying sections of code and just updating several parameters, then that sounds like you need a method. I’ve often created something similar to the following as part of my tests.

[TestFixtureSetUp] 
public void TestFixtureSetUp() 
{ 
  var parent = Sitecore.Context.Database.GetItem(/sitecore/content/home”); 
  var item = CreateItem(“myitem”, parent); 
  var child1 = CreateItem(“child 1, item); 
  var child2 = CreateItem(“child 2, item); 
} 

private Item CreateItem(string name, Item parent) 
{ 
  var template = Sitecore.Context.Database.GetTemplate(“mytemplate”); 
  var item = parent.Add(name, template); 

  item.Editing.BeginEdit(); 
  item[“title”] = name; 
  item[“text”] = “some random text”; 
  item.Editing.EndEdit(); 
}

This helps with removing clutter from your code.

This above example is fine when you’re only dealing with simple data in the fields and don’t need to insert links and references to other items. It’s still all possible to do that in code, but I was looking for an easier way. I wanted to leverage the Sitecore tools to create the content such as the content editor or even the page editor.

My new idea was to use the content editor to create the test content structure, then grab the XML for the subtree of content and save it in a file. I can then use that XML to recreate the subtree at any time as part of my test.

So first things first, I need to create some test content in Sitecore. I’m sure you know how to do that Smile . Make sure you create your test content under a single root item. This makes things later on much easier.

Now to get the XML for the content. There are so many ways to do this, not to mention there is a method on the Item class to get the XML in code. I find the easiest way to get an item’s XML is to use Revolver or Sitecore Rocks.

In Revolver I can use the GetItem (gi) command to get the XML for an item. Passing the recursive parameter (-r) I can include the items subitems as well. Combine that with the echo command and I can write that XML directly out to a file.

cd (/sitecore/content/home/test root) 
echo -f c:\temp\content.xml < (gi -r)

If you don’t have Revolver you can use the Sitecore Rocks Visual Studio plugin. Just navigate to your content in the Sitecore explorer, right click on your item then select XML and select one of the options to open the XML (and select and copy it), or open the copy XML dialog.

sc rocks xml

Either way you’ll be able to copy the XML to the clipboard, then create an XML file in your test project and paste the XML content in it.

Make sure you add the XML file to your test project.

Now to create the content from the XML file. This process simply involves grabbing the item to paste the content below, getting the XML content and “pasting” it into the item to have it imported into the content tree. The following code snippet assumes you’re using the codeflood NUnit test runner or some other test runner which runs inside Sitecore and allows you to call the Sitecore API from your test code.

var home = Sitecore.Context.Database.GetItem("/sitecore/content/home");
using (new SecurityDisabler())
{
  home.Paste(
    File.ReadAllText(
      HttpContext.Current.Server.MapPath("~\test data\content.xml")),
    false, PasteMode.Overwrite);
}

m_testRoot = home.Axes.GetChild("testroot");

I’m using a SecurityDisabler above cause my test is running in the context of the live website so my context (by default) will be the web database and extranet\anonymous user.

The testroot item above would have been defined inside the XML I pasted. Having a single item at the root in your test content makes it much easier to retrieve your test content so you can store local variables of the items to test against and also to cleanup after you finish your test. All I have to do is delete the m_testRoot item above.

Now I have a known content structure with expected field values I can write my tests against that content, confident in what to expect in the results.

And because the content creation process is now so easy it’s more likely you’ll create other, different content structures to ensure you’re tests cover a wider range of possible content inputs. Happy Testing!

Comments

Vladimir Levchuk

Cool. But for unit testing I'd expect more clear solution where you don't need to start sitecore at all instead of trying to add test data - do you have experience with mocking Sitecore API?

Alistair Deneys

Hey Vladimir, I have been able to get the Sitecore API to run outside of an HttpContext, so Sitecore isn't actually running. But I normally still use a real Sitecore database. I have some very strong opinions that you shouldn't mock anything outside your control that you can't change. When you mock an entire system such as SharePoint or Sitecore you're codifying a bunch of assumptions and understandings about that system. If any of your assumptions are wrong, then your tests may be passing even though they wouldn't pass against the real system. I think mocking has it's place when you control all the pieces, just not when you're trying to emulate a complex external system.
When it comes down to it, I'm not too concerned about "unit" test or "integration" test, I just want automated tests. Something I just can push a button on and a bunch of repeatable, reliable tests will run and validate for me that my code is working as expected.
Sitecore 7 will include better support for testing, being able to swap out system components and mock them. This will be done (from my understanding) by utilising interfaces much more. Let's see if I can be persuaded over to the mocking camp when Sitecore 7 is released :)

Really solid post. I my self i looking into how to do automated tests with Sitecore. I like the idea of using the xml template to import valid testdata. With that said I think it was Roy Osherove who said that test that involved changing or adding thinks in external system such as Database i to be considered more integration test. And yes it is possible to have a setup where you can query sitecore without a valid httpcontext.

Leave a comment

All fields are required.