Complete Unit Testing In Sitecore

Unit testing is Sitecore can be tricky. There are a lots of moving parts. And to truly be able to test, you need to somehow bring all of those parts into a test.

Unit testing could be divided into two different type: regular Unit Tests (just to test BusinessLogic without data relations) and Integration Unit Tests.

In the current article I will focus on Integration Unit Tests. In the futher articles I will describe how to write UnitTests without strong coherence with Sitecore.

So, to accomplish the main goal and have working UnitTests I will try to do the following:

  • Execute unit tests within a valid Sitecore context
  • Load all current App_Config/Include/ configs at runtime
  • Access to all APIs
  • Maintain to additional config files
  • No scripting involved

According to our goals let’s find a way of how to reach it. It’s mostly screenshots and code samples anyways.

Create a New Test Project

Firstly, I will create an Integration Unit Test project. As basis I will use standard ClassLibrary project type.

Add New Project

So, as you see the name of ot is pretty simple – AndreyV.Sitecore.IntegrationTests.

Integration with Sitecore

To allow IntegrationUnitTests project to use all benefits from Sitecore let’s provide for it all needed configuration files.

The following steps are focused to have the similar configuration structure inside the tests as Sitecore has.

app.config

  • Copy & paste your web.config from your website into IntegrationTests project.
  • Rename it app.config.
  • Check the app.config‘s properties (alt+enter). Ensure that Copy to Output Directory is set to Copy Always.

A Class Library uses an app.config instead of a web.config. Hopefully, we’re following best-practices and modifying web.config indirectly via /App_Config/Include/

As rule, we want to make sure every config we drop in here is set to Copy.

SetToCopy

Recreate the App_Config folder structure

We’ll create a folder structure inside AndreyV.Sitecore.IntegrationTests, mimicking \Website\App_Config:

- App_Config
    - Include
    - Prefetch
    - Security

Recreate The App_Config Folder Structure

Add Config files as a link

Now I have faced with the need of having the same set of config files as in Web project exist. For sure, I can copy&paste all of these files. But after it will be a headache to maintain consistent of them and make all changes inside any config file twice! No really, thank you. Instead of this I will use a wee bit of magic. For this I use auxiliary option – Add As Link:

  • Right-click App_Config inside to the IntegrationTests project
  • Add > Existing Item…
  • Navigate to file explorer to Website\App_Config
  • Use an asterisk to show all the config files.

Add Config Files As A Link

  • Select all config files but not folders (Shift can help us)
  • Note the down arrow beside “Add” > click > Select Add As Link

Repeat these step with the App_Config/Include, App_Config/Prefetch & App_Config/Security folders respectively.

Set all linked configs to Copy

One final step, super important. Highlight all the config files in each folder under App_Config change Copy to Output Directory setting to Copy if newer.

Be sure to Save All on the solution after doing this.

Set All Linked Configs To Copy

All Configs Set As Linked

Notice all files have been added. They have a special icon indicating that they’re linked.

UnitTests writing

Now Integration project contains all necessary config files (that are needed to communicate with Sitecore) and I can start writing of UnitTests.

During the last few years I have been using NUnit framework for writing UnitTests. This platform provides a lots of abilities to write tests with various configuration for them.

Add NUnit via NuGet

  • In IntegrationTests project, right-click References
  • Click Manage NuGet Packages…
  • Search for nUnit using the search box. Add it.

Add NUnit Via NuGet

  • Rock and roll.

Add Sitecore DLL References

In IntegrationTests project, under References, add a local reference to Sitecore.Kernel.dll & Sitecore.Nexus.dll. Make sure Copy Local is set to True for in the properties.

Change Sitecore’s Root Path During Tests

This step is bit of a black magic. I’m using ability of NUnit to do a particular action before tests are run. Inside this action I will reassign Sitecore’s file system to run from the IntegrationTests project build folder:

  • Create a new Class called TestSetupFixture.cs in the IntegrationTests project.
  • Copy & paste the below code.
  • See inline comments for a brief explanation

TestSetupFixture.cs

namespace AndreyV.<span class="mceItemHidden" data-mce-bogus="1">Sitecore</span>.IntegrationTests
{
    using System;
    using System.IO;
    using global::Sitecore.Configuration;
    using global::Sitecore.Globalization;
    using global::Sitecore.SecurityModel;
    using NUnit.Framework;
    using Sitecore = global::Sitecore;

    // In NUnit, this ensures it will run before any/all tests are executed
    [<span class="hiddenSpellError" pre="" data-mce-bogus="1">SetUpFixture</span>]
    public class <span class="hiddenSpellError" pre="class " data-mce-bogus="1">TestSetupFixture</span>
    {
        public static <span class="hiddenSpellError" pre="static " data-mce-bogus="1">SecurityDisabler</span> <span class="hiddenSpellError" pre="SecurityDisabler " data-mce-bogus="1">Disabler</span>;

        [SetUp]
        public void <span class="hiddenSpellError" pre="void " data-mce-bogus="1">SetupTest</span>()
        {
            try
            {
                // This grounds Sitecore in the current directory so when
                // Sitecore.IO.FileUtil.MapPath runs, it can find the files.
                State.HttpRuntime.AppDomainAppPath = Directory.GetCurrentDirectory();

                // This static.  Allows it live, avoiding garbage collection
                Disabler = new SecurityDisabler();

                // If you need to run pipelines do it <span class="hiddenSpellError" pre="it " data-mce-bogus="1">hear</span>.
                //CorePipeline.Run("initialize", new <span class="hiddenSpellError" pre="new " data-mce-bogus="1">PipelineArgs</span>());

                // Set any properties you need in content
                Sitecore.Context.SetLanguage(Language.Parse("en"), false);
            }
            catch (Exception ex)
            {
                Console.WriteLine(ex.Message);
                throw;
            }
        }
    }
}

If you don’t use NUnit, you’ll have to find an equivalent methodology. Or simply run the above code manually at the start of your tests.

Create simple UnitTest

Now I’ll create a test to prove that my approach works. Let’s call it SimpleTests.cs. Decorate the class as a [TestFixture] and the test method with [Test].

This test will prove the following:

  • The configs /App_Config/Include are being processed. The information we’re looking for is coming from /App_Config/Include/SiteDefinition.AndreyV.config
  • The Sitecore Context does exist
  • Context-specific values can be set, retrieved
  • Databases are active

SimpleTest.cs

namespace AndreyV.Sitecore.IntegrationTests
{
    using NUnit.Framework;
    using Sitecore = global::Sitecore;

    [TestFixture]
    public class SimpleTests
    {
        [Test]
        public void ShouldFindTheHomeNode()
        {
            // Get AndreyV is declared under:
            // /App_Config/Include/SiteDefinition.AndreyV.config
            Sitecore.Context.SetActiveSite("AndreyV");

            // Pull the start path of the site
            string startPath = Sitecore.Context.Site.StartPath;

            // Pull the database name
            string databaseName = Sitecore.Context.Site.Database.Name;

            // Load the web database, and get item
            var db = Sitecore.Data.Database.GetDatabase("web");
            var item = db.GetItem("/sitecore/content/AndreyV/home");

            // Found the home node
            Assert.That(item, Is.Not.Null);

            // Paths of the home items match
            Assert.That(startPath.ToLower(), Is.EqualTo(item.Paths.FullPath.ToLower()));

            // Database name pulled from context matches too 
            Assert.That(databaseName, Is.EqualTo(db.Name));
        }
    }
}

Almost there!

Potential Issues

If your file system paths in your web.config are absolute, they may need adjusting. Prefixing paths with a dot ./App_Config/ is all that’s usually required. Definitely beware of the paths.

Also – be sure to Rebuild your IntegrationTests project in Visual Studio before you run your tests. The Rebuild copies your App_Config folder to the tests bin folder. For some reason, running tests using NUnit doesn’t copy them.

Closing Thoughts

To recap fundamental principles here:

  • Renamed your web.config to app.config
  • Include all config files in your IntegrationTests project as Links
  • Run a line of a voodoo code that dynamically sets Sitecore’s current directory

This looks like a bit of work but once you understand the steps, it’s quick process. And being able to test with a context and all your configs is defintely worth the effort.

Enjoy!

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