Unit testing in Sitecore can be tricky. There are a lot of moving parts. And to truly be able to test, you need to somehow bring all of those parts into a test.
That's exactly what I'm attempting do.
- 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
At high-level we'll accomplish this by:
- Renaming your *web.config* to *app.config*
- Including all config files in your **IntegrationTests** project as **Links**
- Run a line of a voodoo code that dynamically sets Sitecore's current directory
Let's do this. It's mostly screenshots and code samples anyways.
Create A New Test Project
Create a new project in Visual Studio for the tests. Make sure it's a standard Class Library. Call it what you want, I like **Fishtank.IntegrationTests**.Create Your App.Config
Steps: - 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, you're following best-practices and modifying your web.config indirectly via */App_Config/Include/* As rule, we want to make sure every config we drop in here is set to **Copy**.Recreate The App_Config Folder Structure
We'll create a folder structure inside **Fishtank.IntegrationTests**, mimicking *\Website\App_Config*:- App_Config - Include - Prefetch - Security
Add Config Files As A Link
Now here comes a wee bit of magic. We need all of our config files in our **IntegrationTests** project but don't want to maintain two copies of them. For this we use **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. - Use *Shift* to highlight all files (not folders) - 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.All Configs Set As Linked
Notice all files have been added. They have a special icon indicating that they're **linked**.Add NUnit Via NuGet
NUnit is my test runner of choice. Feel free to divert from the step is you have your own favorite. Here are the steps. - In your **Integration** project, right-click **References** - Click **Manage NuGet Packages...** - Search for **nunit** using the search box. Add it. - Rock and roll.Add Sitecore DLL References
In your **Integration** 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 dark art. We're make a class that executes before any test(s) are run. It reassigns 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**
using System;
using System.IO;
using NUnit.Framework;
using Sitecore;
using Sitecore.Configuration;
using Sitecore.Globalization;
using Sitecore.SecurityModel;
namespace Fishtank.IntegrationTests
{
// In NUnit, this ensures it will run before any/all tests are executed
[SetUpFixture]
public class TestSetupFixture
{
public static SecurityDisabler _disabler;
[SetUp]
public void SetupTest()
{
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 hear.
//CorePipeline.Run("initialize", new PipelineArgs());
// Set any properties you need in content
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 A Unit Test To Prove It Works
Now we'll create a test to prove that we're up and running. 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.getfishtank.ca.config* - The Sitecore Context does exist - Context-specific values can be set, retrieved - Databases are active
using NUnit.Framework;
namespace Fishtank.IntegrationTests
{
[TestFixture]
public class SimpleTests
{
[Test]
public void ShouldFindTheHomeNode()
{
// Get getfishtank is declared under:
// /App_Config/Include/SiteDefinition.getfishtank.ca.config
Sitecore.Context.SetActiveSite("getfishtank");
// 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/fishtank/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!
Proof That It Worked
For proof, this is the best I can reasonably do. An all encompassing screenshot showing: - Visual Studio - Open *SimpleTest.cs* test file - Open site defintion file, */App_Config/Include/SiteDefinition.getfishtank.ca.config* - NUnit test runner window showing a **passed test** - **Properties** window showing that the linked config resides in */App_Config/Include/*.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. If you this is your problem, you'll see these errors:TestFixtureSetUp failed in TestSetupFixture Exception doesn't have a stacktrace
Could not find a part of the path 'C:\getfishtank\Fishtank.IntegrationTests\bin\Debug\app_config\prototypes.config'.