codeflood logo

Automated Testing in Sitecore Without an HttpContext

This year was the first year that Sitecore’s Dreamcore conference was held in Australia. And I for one jumped at the opportunity to speak at it. One topic that quite interests me is unit testing, and if you’ve read my posts over the years you’d see I have come up with a variety of techniques for getting unit testing (or rather, integration testing) working for Sitecore projects. So what better topic to talk about at Dreamcore than automated testing techniques?

My session at Dreamcore covered a wide range of techniques and so I didn’t have time to go in depth with any single technique such as this one. In fact, I only had 40 minutes to speak and my rehearsal the night before I came in at 1 hour. So I’ll take this opportunity to explain the technique in depth.

Many years ago I tried to run my automated tests for my Sitecore project’s inside the NUnit GUI test runner…and failed. The issue with the standalone test runner is that there is no HttpContext, and as I’m sure you’re well aware, Sitecore requires one of these to work. When I ran into this hurdle all those years ago, I instead developed different techniques to get around this limitation.

Anyway, back to this year. A few months before Dreamcore I was thinking I had to have another look into getting my automated tests for Sitecore running without the HttpContext. And then Mike Edwards beat me to the punchline and posted a blog entry on how he got unit testing for Sitecore working in the NUnit GUI. Mike’s post proved to me that it was possible, so I followed his post and got some tests running inside the NUnit GUI test runner, calling into the Sitecore 6.5 API.

With Mike’s general direction in place I set about fully integrating this approach to testing into a project I was working on at the time, which would provide a bit more context for me when I was writing my Dreamcore presentation.

The main change I made to Mike’s approach was to copy the configuration over from an existing Sitecore project. This way if any of the Sitecore configuration changed my test project would also see that change and the code under test would be run in an environment as close as possible to production.

First thing’s first, create a new class library project to write the tests in and add references to the nunit.framework assembly. As we’ll be calling into the Sitecore API (the whole point of this exercise) we’ll also need to add a reference to the Sitecore.Kernel assembly from the Sitecore instance the tests will be written against. To make sure we’ve got the configuration right to use the Sitecore API we’ll also create a simple test which uses the Sitecore API. The simplest thing I can think of would be grabbing a field from the home item. The following code shows how to implement this test.

[TestFixture]
public class ApiTest
{
	[Test]
	public void AccessFieldOnHome()
	{
		var db = Sitecore.Configuration.Factory.GetDatabase("web");
		var home = db.GetItem("/sitecore/content/home");
		var fieldValue = home["title"];
		Assert.AreEqual("Sitecore", fieldValue);
	}
}

The above code will also require a reference to System.Configuration due to the call to the Sitecore.Configuration namespace.

Note above how we need to retrieve the database and cannot use the Sitecore context to get the context database because the context hasn’t been populated. The Sitecore context is populated through the httpRequestBegin pipeline which isn’t run when we’re calling the API outside an HttpContext.

If we were to run the above test we would end up with that familiar “Failure: System.InvalidOperationException : Could not read Sitecore configuration.” error. Have you ever noticed how so many people lack the ability to read and interpret error messages? The above error message gives a clear description of the issue…there is no Sitecore configuration.

So onto the biggest hurdle, copying the Sitecore configuration over from the Sitecore project which these tests will exercise. I want to automate this process and make it part of the project build process so I get any configuration updates in the test project. This will require some MSBuild additions.

To edit the test project’s MSBuild unload the project in Visual Studio then right click the project and select “Edit TestProject.csproj”. Alternatively you could directly edit the project file (csproj) using a separate text editor and Visual Studio will prompt you to reload the project file when you go back into Visual Studio. However the benefit of editing the project file inside Visual Studio is that Visual Studio will provide IntelliSense as you edit the file.

To make maintenance easier we’ll store the path to the Sitecore instance we’ll be using the configuration from in a variable. While editing the project file add the following script to the first PropertyGroup element which doesn’t have any additional attributes.

<SitecorePath>C:\inetpub\WeBlog-65\Website</SitecorePath>

Make sure you update the path in the variable to reference the local path on your machine. The above script defines a variable called SitecorePath and sets it to the path provided. This variable can then be used throughout the rest of the script.

Now Find the AfterBuild target which will be commented out. This target will be called by Visual Studio if the build was successful. This is where we’ll put our additional MSBuild configuration. Uncomment the AfterBuild target and add the following script to it.

    <Copy SourceFiles="$(SitecorePath)\web.config" DestinationFiles="$(OutputPath)\$(AssemblyName).dll.config" />

The above MSBuild script will copy the web.config of the referenced Sitecore instance to the configuration file of the test assembly. Remember for Windows assemblies the configuration file is not named web.config but is instead the same name as the assembly with an additional .config at the end. So to provide configuration for my program MyApp.exe the configuration file must be named MyApp.exe.config. The same works for the test assemblies.

Sitecore configuration is actually spread over a number of additional files, so the next piece of script will grab those files and copy them over to the appropriate location as well.

<CreateItem Include="$(SitecorePath)\App_Config\**\*.*">
	<Output ItemName="configFiles" TaskParameter="Include" />
</CreateItem>

<!-- Copy relative external source files -->
<Copy SourceFiles="@(configFiles)" DestinationFolder="$(OutputPath)\App_Config\%(RecursiveDir)" />

Note how in the DestinationFolder attribute I’m using the RecursiveDir well-known metadata about the input files. This will create the same directory structure as the source files are in instead of dumping them out to a flat directory.

To make sure we have all the assemblies we might require we will also need to copy all the assemblies in the Sitecore bin folder to the same location as the test assembly.

<CreateItem Include="$(SitecorePath)\bin\*.dll">
	<Output ItemName="binaryFiles" TaskParameter="Include" />
</CreateItem>

<Copy SourceFiles="@(binaryFiles)" DestinationFolder="$(OutputPath)" />

And now, the test will work!

Though we’re missing one important piece of configuration…configuration include files. Configuration includes files are located in the \App_Config\Include folder of a Sitecore instance and allow patching the Sitecore configuration. This makes it much easier to keep your project configuration separate from the Sitecore configuration. It also makes upgrading Sitecore much easier as you can simply take the entire updated web.config file (Sitecore section) without worry that you’re reverting some update made for your project.

There are 2 options for handling the configuration include files. Firstly, don’t use them. For a test project you could just make sure all the configuration required is contained in the single web.config file. But that’s going against what I’ve been talking about in terms of separating project configuration from Sitecore configuration and using a real project’s configuration to ensure the tests are run in a realistically configured environment.

The other option is to put the include files where they’re required to allow the configuration patching utility to find them. The script above is already taking care of copying the include files to the output folder, so what else needs to be done?

The configuration patch utility used by Sitecore internally contains a call to the Server.MapPath() method. When there is no HttpContext this method will simply pass back the path as it was passed in. Sitecore is trying to locate the folder relative to the web application root, so what’s passed to the method is /App_Config/Include. When trying to locate this path on a disk the leading slash will cause the OS to look from the root of the disk, so the path being located is actually c:\App_Config\include. To make the configuration include files work we’ll need to copy the files to a folder off the root of the disk named App_Config.

    <Copy SourceFiles="@(configFiles)" DestinationFolder="c:\App_Config\%(RecursiveDir)" />

Now the configuration patch utility will work.

Some points of caution when using this technique. Some components won’t be in the correct state when you try and use them because none of the request process has been run. No ASP.NET events and no Sitecore pipelines have been run. This is why the Sitecore context is not populated. But I’ve also found some other classes such as Sitecore.Globals aren’t initialised. If your test uses any of the global objects (Links DB, Tasks DB) you’ll need to manually initialise them with a call to Sitecore.Globals.Load(); before using them. I’ve recently spoken to one of the Sitecore Users Virtual Group users who has been using this technique who had issues calling Delete() on an item, because of the uninitialised Tasks database.

Another method I’ve found which simply won’t work is the WebControl.RenderAsText() method. This method relies on there being an HttpContext and simply won’t work without one.

The above points of caution are just a few I’ve found whilst using this technique. That’s not to say there aren’t a heap of other classes which need initialising before use that I haven’t run into yet.

This technique is also quite heavy. The Sitecore application is actually running. A real Sitecore database it accessed. Many of the objects defined in the configuration are created and strung together. Although anecdotally comparing the speed of the test runs using this technique to my normal technique of using an embedded test runner, this technique does seem faster.

If you’d like a working example of this technique you can refer to my materials from Dreamcore Australia 2011. I was lucky enough to recently deliver the same presentation to the Sitecore Users Virtual Group, so if you’d like to see me put those pieces together then you can refer to the recording of my Testing Strategies for Sitecore presentation on Hedgehog’s YouTube channel.

Comments

[...] / Jenkins There is also this interesting article about Sitecore Unit testing without a HTTP [...]

[...] There is also this interesting article about Sitecore Unit testing without a HTTP [...]

Thanks for the post, Alistair. The automated copy of configuration files is very clean. I think there may be a step missing, however. When I run the test, I am receiving this error: : System.IO.DirectoryNotFoundException : Could not find a part of the path 'C:\app_config\prototypes.config'.
The Mike Edwards blog post (http://www.experimentsincode.com/?p=232) indicates that all "/App_Config" references need to be changed to ".\App_Config". Does this step need to be added to the AfterBuild target?

A great post and very useful build script. I've one point to add: iIf you set Sitecore.Context.IsUnitTesting = true, Sitecore will look for App_Config/Include files in the test project directory. I modified your build script to copy App_Config to both the Output directory and the Project directory, and I am able to test with full access to App_Config/Include files. I've written a couple of blog posts on this:
http://www.dansolovay.com/2013/01/sitecore-nunit-testing-simplified.html and http://www.dansolovay.com/2013/01/sitecore-unit-testing-and-auto-include.html

Alistair Deneys

Nice find Dan

Alistair Deneys

Hi Dan, In my testing I didn't find that I needed to swap the slashes. I wonder if Windows version plays a part in this issue.

Andy

Great post Alistair! I added the post build task to the project file as you described in this blog post. I also made the changes Dan Solovay suggested to make the patching process look for the app_config/include files in the test project directory. When I build the test project all the files are being copied to the right places and I'm also able to run the tests in debugger (Test-&gt;Debug-&gt;All Tests). However, when I run the test in Visual Studio Test Explorer (or by using the Test-&gt;Run-&gt;All Tests option in the menu), tests fail and the following exception is thrown.
System.TypeInitializationException : The type initializer for 'Sitecore.Diagnostics.LoggerFactory' threw an exception. ----&gt; System.NullReferenceException : Object reference not set to an instance of an object. Result StackTrace: at Sitecore.Diagnostics.LoggerFactory.GetLogger(Type type)

Andy

Just wanted to add that the following line in the test method throws the exception I mentioned above:
var db = Sitecore.Configuration.Factory.GetDatabase("master");
Any ideas why would the tests run fine in the debugger but not in the test explorer?
Thanks!

Alistair Deneys

Hi Andy, Sorry for taking so long to respond, I completely missed the email notification for your comment.
The problem sounds like the VS test runner may be looking for config in a location and it's not finding it. Like perhaps the config copying is only occurring during debug and not during release build (I can't see how that could happen, but it's something to check out). I recently presented this technique at the Sitecore Virtual Users Summit with what may be slightly simpler configuration. I also used points from Dan so I didn't have to worry about copying the config to the root of the drive. You can find the completed solution at http://www.codeflood.net/files/automated-testing-sitecore-v2-code.zip . Download it and give the NoHttpContext_* tests in the Test project a go and see if they work. The build will require a working instance of Sitecore on your machine to be configured in the deploy.targets file.
I've not used the integrated test features of VS2012 but I did confirm that my "without HttpContext" tests from the presentation would run properly using the "NUnit Test Adapter" extension from the extensions gallery. I had my tests running from the test explorer.

Mark

Alistair the urls to Mike Edwards blog have changed to http://www.experimentsincode.com and http://www.experimentsincode.com/?p=232
Thanks and Regards Mark

Alistair Deneys

Thanks Mark, The links have been updated.

Leave a comment

All fields are required.