Loading...

Custom Sitecore 404 Page in a controller and writing a unit test

Recently I had to create a custom pipeline for a 404 page in Sitecore and write a unit test for it, so I did some research to see what is out there these days. Most articles discuss how to implement 404 in the pipeline only and that approach is fairly well documented on the web. I am going to demonstrate how to implement 404 page in a controller action and then we are going to write a unit test for it.

Let's start

I am using the same sample project as I did in my previous article here.

NotFound404Processor

First things first, we are going to need a pipeline in which we are going to identify when we are missing an item and load the 404 page which will be rendered through the controller action. So here we go.


using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Net;

using Sitecore;
using Sitecore.Data;
using Sitecore.Data.Items;
using Sitecore.Pipelines.HttpRequest;
using Sitecore.Diagnostics;

namespace Local.Sitecore.Domain.Pipelines
{
    public class NotFound404Processor : HttpRequestProcessor
    {
        public override void Process(HttpRequestArgs args)
        {
            // Asserts that the arguments are not null
            Assert.ArgumentNotNull(args, "args");

            // ensure we are good to proceed
            if (Context.Item != null || Context.Site == null || Context.Database == null) return;

            // get 404 item
            var notFoundItem = Context.Database.GetItem(Consts.NotFoundItemId);
            
            // set current sitecore context item
            Context.Item = notFoundItem;
        }
    }
}

Pipeline configuration 

Don't forget to add this new pipeline in the configuration. Here is the standard configuration setting to add above processor after the ItemResolver pipeline.


<?xml version="1.0"?>
<configuration xmlns:patch="http://www.sitecore.net/xmlconfig/">
  <sitecore>
    <pipelines>
      <httpRequestBegin>
        <processor patch:after="*[@type='Sitecore.Pipelines.HttpRequest.ItemResolver, Sitecore.Kernel']"
                   type="Local.Sitecore.Domain.Pipelines.NotFound404Processor, Local.Sitecore.Domain" />
      </httpRequestBegin>
    </pipelines>
  </sitecore>
</configuration>

ErrorController

So far so good. Here is our ErrorController with the Rendering action which sets proper StatusCode and renders 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;

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

        // GET: Rendering
        public ActionResult Rendering()
        {
            var currentItem = base.SitecoreContext.GetCurrentItem<ErrorPage>();

            var model = new Local.Sitecore.Website.ViewModels.ErrorPageViewModel()
            {
                ErrorTitle = currentItem.ErrorTitle,
                ErrorDescription = currentItem.ErrorDescription
            };

            Response.TrySkipIisCustomErrors = true;
            Response.StatusCode = currentItem.ErrorCode;

            return View(model);
        }
    }
}

Confirming 404 response with Fiddler

When all of the above is implemented we can verify we are getting proper 404 response in the fiddler.

ErrorControllerTests

Finally here is ErrorControllerTests class with the unit test.


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

using Microsoft.VisualStudio.TestTools.UnitTesting;
using Moq;
using Glass.Mapper.Sc;
using Glass.Mapper.Sc.Web;
using Local.Sitecore.Website.Controllers;

namespace Local.Sitecore.UnitTests.Controllers
{
    [TestClass]
    public class ErrorControllerTests
    {
        [TestMethod]
        public void Rendering_IfErrorCode404_Response_404()
        {
            // ------------------------------------------
            // ARRANGE
            var errorPage = new ErrorPage() { ErrorCode = 404 };

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

            ErrorController controller = new ErrorController(mockSitecoreContext.Object);

            Mock<HttpContextBase> mockHttpContext = new Mock<HttpContextBase>();
            Mock<HttpRequestBase> mockRequest = new Mock<HttpRequestBase>();
            Mock<HttpResponseBase> mockResponse = new Mock<HttpResponseBase>();
            mockHttpContext.Setup(x => x.Request).Returns(mockRequest.Object);
            mockHttpContext.Setup(x => x.Response).Returns(mockResponse.Object);
            mockResponse.SetupAllProperties();

            RequestContext rc = new RequestContext(mockHttpContext.Object, new RouteData());
            controller.ControllerContext = new ControllerContext(rc, controller);

            // ------------------------------------------
            // ACT
            var result = controller.Rendering() as ViewResult;

            // ------------------------------------------
            // ASSERT
            Assert.IsTrue(controller.Response.StatusCode == 404);
        }
    }
}

 That's all. Hopefully it all makes sense as I didn't go too much into details. Let me know if you have any questions.

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.