How to Unit Test a GlassController Action which Returns a View Taking a Model

474 views Asked by At

I'm a sitecore developer and I want to create a sample sitecore helix unit testing project for testing out our "HomeBottomContentController" controller:

    public class HomeBottomContentController : GlassController
    {
        private readonly ISitecoreContext _iSitecoreContext;
        public HomeBottomContentController(ISitecoreContext iSitecoreContext)
        {
            _iSitecoreContext = iSitecoreContext;
        }

        public override ActionResult Index()
        {
            var model = _iSitecoreContext.GetCurrentItem<Home_Control>();
            return View("~/Views/HomeBottomContent/HomeBottomContent.cshtml", model);
        }
    }

I have created a WTW.Feature.HomeBottomContent.Tests project, for the purpose of testing this entire component using helix unit testing. In it I have a UnitTest1.cs file with following:

namespace WTW.Feature.HomeBottomContent.Tests
{
    [TestClass]
    public class UnitTest1
    {
        [TestMethod]
        public void Test_ISitecoreContextInsertion()
        {
            var iSitecoreContext = Mock.Of<Glass.Mapper.Sc.ISitecoreContext>();
            HomeBottomContentController controllerUnderTest = new HomeBottomContentController(iSitecoreContext);
            var result = controllerUnderTest.Index() as ViewResult;
            Assert.IsNotNull(result);
        }
    }
}

This test does pass, meaning "result" is NOT null; however, the problem is when I step into the Index() code, I see that the "model" variable is NULL when we do

    var model = _iSitecoreContext.GetCurrentItem<Home_Control>();

My question is, how exactly do I change this code to make sure that the "model" in that line does not become null? How do I "mock" an item in unit test code for the _iSitecoreContext so that it has a "Home_Control" template with legit values for its fields? Would that even be the right approach? Most online sources I've found do not have a similar scenario, I'm looking for the shortest code possible.

Another question I had is, how can I test the below Index() method in my [TestMethod], given that the SitecoreContext is declared inside the Index() method, rather than received in the HomeBottomContentController constructor like above? Is there a way to do that from the [TestMethod], or we have to send in the SitecoreContext into the HomeBottomContentController constructor or into the Index() method as a parameter?

public override ActionResult Index()
{
    var context = new SitecoreContext();
    var model = context.GetCurrentItem<Home_Control>();
    return View("~/Views/HomeBottomContent/HomeBottomContent.cshtml", model);
}
2

There are 2 answers

0
Nkosi On BEST ANSWER

In that case you would need to mock the desired behavior on the mocked dependency

[TestClass]
public class UnitTest1 {
    [TestMethod]
    public void Test_ISitecoreContextInsertion() {
        //Arrange
        var model = new Home_Control() {
            //...populate as needed
        }
        var iSitecoreContext = new Mock<Glass.Mapper.Sc.ISitecoreContext>();
        //Setup the method to return a model when called.
        iSitecoreContext.Setup(_ => _.GetCurrentItem<Home_Control>()).Returns(model);
        var controllerUnderTest = new HomeBottomContentController(iSitecoreContext.Object);

        //Act
        var result = controllerUnderTest.Index() as ViewResult;

        //Assert
        Assert.IsNotNull(result);
        Assert.IsNotNull(result.Model);
        //...other assertions.
    }
}

UPDATE

Creating the context within the action tightly couples it to the context, making it almost impossible to mock. That is the reason explicit dependencies are injected

1
Aleksey Shevchenko On

You can do something like that:

public class HomeBottomContentController : GlassController
{
    private readonly ISitecoreContext _iSitecoreContext;
    public HomeBottomContentController(ISitecoreContext iSitecoreContext)
    {
        _iSitecoreContext = iSitecoreContext;
    }

    public override ActionResult Index()
    {
        var model = this.GetCurrentItem();
        return View("~/Views/HomeBottomContent/HomeBottomContent.cshtml", model);
    }

    protected virtual Home_Control GetCurrentItem() 
    {
        return _iSitecoreContext.GetCurrentItem<Home_Control>();
    }
}

namespace WTW.Feature.HomeBottomContent.Tests
{
    [TestClass]
    public class UnitTest1
    {
        [TestMethod]
        public void Test_ISitecoreContextInsertion()
        {       
            var iSitecoreContext = Mock.Of<Glass.Mapper.Sc.ISitecoreContext>();
            var controllerUnderTest = new FakeHomeBottomContentController(iSitecoreContext);
            var result = controllerUnderTest.Index() as ViewResult;
            Assert.IsNotNull(result);
        }
    }

    public class FakeHomeBottomContentController : HomeBottomContentController 
    {
        public FakeHomeBottomContentController(ISitecoreContext iSitecoreContext) : base(iSitecoreContext) 
        {
        }

        protected override Home_Control GetCurrentItem()
        {
            // return instance of Home_Control type
            // e.g.         
            return new Home_Control();
        }
    }
}