Unit Testing Sitecore

Unit testing is a very popular topic, and is a very good practice to adopt.  Unfortunately, as anyone who has worked with it will know, some common Sitecore classes (Sitecore.Data.Items.Item to name one) can’t be mocked with some popular mocking frameworks, and can’t be directly instantiated.  So, the logical solution (to me) is to let your unit tests access the Sitecore API and databases (some others abstract the detail away, but as the saying goes: “Who guards the guards?”).  There have been quite a few different blog articles written about this topic, and people have approached the problem differently. So I thought that I’d share my approach to writing unit tests for Sitecore solutions.

I have three main “requirements” for my solution:

  1. I want configuration to be mostly hands-off. Many solutions that I’ve seen either do not support configuration include files (of which I am incredibly fond) or require an absolute path to be configured somewhere. I want to have my configuration preserved, and I also don’t want to have to make the same change to multiple files (because, sooner or later, someone forgets).
  2. I don’t want to use a web test runner. This is quite a common practice, in part because it allows you to test WebForms components to some degree, and in part because it grants you a populated Sitecore context. However, I want to stick as close as I can to the bare metal.
  3. I’m more interested in testing my application logic at this point, so most of the time I just want an Item object that I’m going to read from. I’m not too fussed about workflows, security, or how the page looks (for the last one, I suggest using Selenium to perform integration tests).

My solution is outlined in the following steps:

  1. Include everything relevant from the App_Config folder as links (this MSDN article explains how), and then set the Copy to output property to Copy if newer. Adding a file as a link creates a “shortcut” to the original file rather than a copy, so I’ve kept to my requirement of not having to maintain files in multiple places.
  2. Create an App.config file based on the vanilla Sitecore Web.config file – after all, you shouldn’t be changing it that much anyway (see: Sitecore Recommended Practices).
    1. From this file, remove everything but the <configurationSections>, <connectionStrings>, <sitecore>, and <log4net> sections.
    2. Optionally, switch the SitecoreLogFileAppender used in the log4net section with a ConsoleAppender – that way log output will show in your unit tests.
  3. Create a fixture set-up method. In NUnit, this is created using the SetUpFixtureAttribute. Add in the following code:
    // Allows Sitecore's calls to Sitecore.IO.FileUtil.MapPath to run.
    // Could alternatively use Assembly.GetExecutingAssembly().CodeBase
    Sitecore.Configuration.State.HttpRuntime.AppDomainAppPath = Directory.GetCurrentDirectory();
    
    // Assign this to a static variable so that it doesn't get accidentally garbage collected
    _disabler = new SecurityDisabler();
    
    // Run Sitecore's initialize pipeline
    CorePipeline.Run("initialize", new PipelineArgs());
    
    // Set a few simple context properties
    Sitecore.Context.Site = Sitecore.Configuration.Factory.GetSite("website");
    Sitecore.Context.Database = Sitecore.Context.Site.Database;
    Sitecore.Context.SetLanguage(Language.Parse("en"), false);
  4. Create a special “Test.config” configuration patch file (in the test project) to do the following:
    1. Remove the following processors from the initialize pipeline:
      • ShowVersion
      • ShowHistory
      • InitializeScheduler
      • InitializeHeartBeat
      • InitializeAgilityPack
      • EnsureAnonymousUsers
    2. Point the LicenseFile setting to wherever you keep your Sitecore license file.

The key point of this post is really the part about setting the AppDomainPath. I’ve found one other article that has gone down a similar route to me, written by Dan Solovay. He solves the problem by setting Context.IsUnitTesting to true. This is certainly neater than my approach, but I’m a bit wary of using it in case it alters other parts of Sitecore’s behaviour – from the checking I’ve done, it shouldn’t affect things that one would typically unit test, but that could change in future releases.

The other thing of note is that I think it’s important to invoke the initialize pipeline. It’s a useful extension point for having things run once Sitecore has started, and one that I personally use quite a lot. If you don’t use it, then I don’t think there’s any harm in leaving it out.

That’s all there is to it!  I hope someone finds this useful.  If some of the steps aren’t clear, then I’ll be keeping an eye on comments for a little while.  As with all internet code, if you use this code and somehow blow up your computer, I accept no responsibility.  I use NUnit, and I’ve tried this successfully with 6.5 and 6.6 – if you use anything else, your mileage may vary.

2 thoughts on “Unit Testing Sitecore

  1. The connection strings are not in the <sitecore /> section. So we need a way to include them in the Web.config. Turns out, this is pretty easy. However, I must warn you here: I did see something that worried me with .NET Reflector in ConfigModifier.dll, so I’m not sure that this solution will never break. It appears to read the connection strings directly from the App_Config/ConnectionStrings.config path. I may be mistaken, and I’ve had no problems with it so far. But if you want to be sure, then skip this step for now.

    1. Hi,

      You’re correct that the connection strings are not located within the <sitecore> section, however you should see in the default web.config file that you have the following line:
      <connectionStrings configSource="App_Config\ConnectionStrings.config" />

      This instructs .Net to read the connection strings from the given configuration source. And, this should be one of the files that you’ve added to the project as a link, which will copy it whenever you build.

      Let me know if you’re still having problems, and I’ll either add some screenshots to illustrate or a sample project file.

      Thanks,
      Matt

Comments are closed.