Loading...

Sitecore Unit Testing with Glass.Mapper, Moq and Visual Studio Test Tools

Unit testing in Sitecore is often overlooked and not used enough. In my experience I have seen quite a few Sitecore projects that did not use unit testing at all or had a unit test project in their solution but never really done anything with it. It kind of just exists there to make an impression it is utilized. Even though it is not as bad as it used to be, it is still rare to see a decent unit testing implementation in an existing Sitecore project.

I am not going to go into depths of explaining unit testing, test-driven development or some other type of testing methodology. There are plenty of articles online for you to read if you are interested to learn more about it. In this article I am going to demonstrate the architecture and give code samples for one way to implement unit testing in your Sitecore solution utilizing new Sitecore 8.2 IoC container, my favorite Sitecore ORM Glass.Mapper, Moq library, TDS and Visual Studio 2015 Test Tools. Details on how the solution is setup is not relevant for the purposes of this article, we are simply only going to look at the unit testing piece and provide a sample.

Let's Begin

We are going to use a home page in an imaginary site to demonstrate how to implement unit testing in Sitecore. If you want to test the code provided here you should have Sitecore 8.2 installed as well as have Sitecore solution setup and ready to go in Visual Studio 2015. How you set it up is up to you as long as you use everything listed in the requirements below.

Requirements:

  • Sitecore 8.2
  • Glass.Mapper 4.2.1.188
  • Moq 4.5.23
  • Team Development for Sitecore (TDS) 5.5.0.15
  • Visual Studio 2015 Test Tools

BaseController

It's always a good idea to have a BaseController in your project and have all your other controllers inherit from the BaseController. As you can see BaseController is inheriting from the GlassController and calling base class constructors. That's all there is to it here.


using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.Mvc;

using Glass.Mapper.Sc;
using Glass.Mapper.Sc.Web;
using Glass.Mapper.Sc.Web.Mvc;

namespace Local.Sitecore.Website.Controllers
{
    public class BaseController : GlassController
    {
        public BaseController(ISitecoreContext sitecoreContext)
            : base(sitecoreContext)
		{ }

        public BaseController(ISitecoreContext sitecoreContext, IRenderingContext renderingContext)
            : base(sitecoreContext, renderingContext)
        { }
    }
}

HomePageViewModel

Here is the HomePageViewModel. Nothing special here as it has only one string property.


using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;

namespace Local.Sitecore.Website.ViewModels
{
    public class HomePageViewModel
    {
        public string PageContent { get; set; }
    }
}

HomePage Glass Model

Here we have auto generated HomePage model from the HomePage template in Sitecore. I've utilized Code Generation Templates within the TDS to generate this code. You can read more about it here. Of course, you can manually construct this class if you wish. We only care about RichTextContent field in this class.


/// <summary>
/// HomePage
/// <para></para>
/// <para>Path: /sitecore/templates/User Defined/Local/Pages/Home Page</para>	
/// <para>ID: aa598b97-deb9-4a71-aa2e-480316a947d5</para>
/// </summary>
[SitecoreType(TemplateId=IHomePageConstants.TemplateIdString,Cachable=true)]
public partial class HomePage  : GlassBase, IHomePage 
{
	/// <summary>
	/// The Rich Text Content field.
	/// <para></para>
	/// <para>Field Type: Rich Text</para>		
	/// <para>Field ID: e2eb1c21-1e1f-4341-94a8-64c960cefa4f</para>
	/// <para>Custom Data: </para>
	/// </summary>
	[global::System.CodeDom.Compiler.GeneratedCodeAttribute("Team Development for Sitecore - GlassItem.tt", "1.0")]
	[SitecoreField(IHomePageConstants.RichTextContentFieldName, ReadOnly = true)]
	public virtual string RichTextContent  {get; set;}

	/// <summary>
	/// The Rich Text Content field IN RAW FORMAT.
	/// <para></para>
	/// <para>Field Type: Rich Text</para>		
	/// <para>Field ID: e2eb1c21-1e1f-4341-94a8-64c960cefa4f</para>
	/// <para>Custom Data: </para>
	/// </summary>
	[global::System.CodeDom.Compiler.GeneratedCodeAttribute("Team Development for Sitecore - GlassItem.tt", "1.0")]
	[SitecoreField(IHomePageConstants.RichTextContentFieldName, Setting = SitecoreFieldSettings.RichTextRaw)]
	public virtual string RichTextContent_Raw  {get; set;}
}

HomeController

Now, here we have our HomeController which inherits from the BaseController. There is a simple Index action uses SitecoreContext from the GlassController to get the current HomePage item. Then we simply set the HomePageViewModel and pass it to the view.


using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.Mvc;

using Glass.Mapper;
using Glass.Mapper.Sc;
using Glass.Mapper.Sc.Web.Mvc;

using Local.Sitecore.Domain.Models.Glass.sitecore.templates.UserDefined.Local.Pages;

namespace Local.Sitecore.Website.Controllers
{
    public class HomeController : BaseController
    {
        public HomeController(ISitecoreContext sitecoreContext)
            :base(sitecoreContext)
        { }

        // GET: Home
        public override ActionResult Index()
        {
            var currentItem = base.SitecoreContext.GetCurrentItem<HomePage>();

            var model = new Local.Sitecore.Website.ViewModels.HomePageViewModel()
            {
                PageContent = currentItem.RichTextContent
            };

            return View(model);
        }
    }
}

Index.cshtml

The view is simply rendering PageContent value.


@inherits Glass.Mapper.Sc.Web.Mvc.GlassView<Local.Sitecore.Website.ViewModels.HomePageViewModel>
@Model.PageContent

HomeControllerTests

Finally, here is HomeControllerTests class with the sample unit test. We are simply testing if PageContent is null. Unit test is setup using Arrange-Act-Assert (AAA) pattern. You can read more about Visual Studio Testing Tools here.


using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Web.Mvc;

using Microsoft.VisualStudio.TestTools.UnitTesting;
using Moq;
using Glass.Mapper.Sc;
using Glass.Mapper.Sc.Web;
using Local.Sitecore.Website.Controllers;
using Local.Sitecore.Domain.Models.Glass.sitecore.templates.UserDefined.Local.Pages;

namespace Local.Sitecore.UnitTests.Controllers
{
    [TestClass]
    public class HomeControllerTests
    {
        [TestMethod]
        public void Index_IsEmpty_PageContent_True()
        {
            // ------------------------------------------
            // ARRANGE
            var homePage = new HomePage() { RichTextContent = string.Empty };

            Mock<ISitecoreContext> mockSitecoreContext = new Mock<ISitecoreContext>();
            mockSitecoreContext
                .Setup(x => x.GetCurrentItem<HomePage>(false, false))
                .Returns(homePage);

            HomeController controller = new HomeController(mockSitecoreContext.Object);

            // ------------------------------------------
            // ACT
            var result = controller.Index() as ViewResult;
            var model = result.Model as Local.Sitecore.Website.ViewModels.HomePageViewModel;

            // ------------------------------------------
            // ASSERT
            Assert.IsTrue(string.IsNullOrEmpty(model.PageContent));
        }
    }
}

That's pretty much it. I hope this all makes sense. I'd like to hear your observations and/or suggestions regarding this implementation.

Disclaimer
This is a personal blog. The opinions expressed here represent my own and not those of people, institutions or organizations that the owner may or may not be associated with in professional or personal capacity, unless explicitly stated.. In addition, my thoughts and opinions change from time to time I consider this a necessary consequence of having an open mind. This blog disclaimer is subject to change at anytime without notifications.