简体   繁体   中英

How to Unit Test a GlassController Action without SitecoreContext Dependency Injection

I'm a sitecore developer, and I want to create a sample sitecore helix unit testing project for testing out the exact below Index() action method of our "HomeBottomContentController" controller, without any dependency injection into a constructor. Note that the commented-out code is exactly what I do NOT want to do.

public class HomeBottomContentController : GlassController
{
    // I want to test the EXACT method below
    public override ActionResult Index()
    {
        var context = new SitecoreContext();
        var model = context.GetCurrentItem<Home_Control>();
        return View("~/Views/HomeBottomContent/HomeBottomContent.cshtml", model);
    }

    /*
    // I do NOT want to have any of the below code for injecting ISitecoreContext into a constructor and testing the IndexTest() below it.
    private readonly ISitecoreContext _iSitecoreContext;
    public HomeBottomContentController(ISitecoreContext iSitecoreContext)
    {
        _iSitecoreContext = iSitecoreContext;
    }

    public HomeBottomContentController()
    { }

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

Here's what I have in my unit testing class. Again, what I have commented out exactly what I don't want to do:

[TestClass]
public class MvcUnitTests
{
    [TestMethod]
    public void Test_HomeBottomContentController_With_ISitecoreContext()
    {
        /*
        // I don't want to do below...
        var model = new Home_Control()
        { Bottom_Content = "XYZ" };

        var iSitecoreContext = new Mock<Glass.Mapper.Sc.ISitecoreContext>();
        iSitecoreContext.Setup(_ => _.GetCurrentItem<Home_Control>(false, false)).Returns(model);

        HomeBottomContentController controllerUnderTest = new HomeBottomContentController(iSitecoreContext.Object);

        var result = controllerUnderTest.IndexTest() as ViewResult;
        */

        //I want to test using the exact constructor below and calling that exact Index() method.  
        HomeBottomContentController controllerUnderTest = new HomeBottomContentController();
        var result = controllerUnderTest.Index() as ViewResult;
        Assert.IsNotNull(result);
        Assert.IsNotNull(result.Model);
        //Assert.AreEqual(((Home_Control)result.Model).Bottom_Content, "XYZ");
    }
}

How can I test my controller's exact Index() method without having to add code to the HomeBottomContentController class that enables dependency injection into a constructor (like the commented-out code above)? I do not want to have to add code to HomeBottomContentController().

@Aleksey Shevchenko If I try your solution, how do I exactly hook up the model to the iSitecoreContext and then assign that to the controllerUnderTest.FakeContext? My code below throws compilation error (You cannot convert from Mock of Glass.Mapper.Sc.ISitecoreContext to Glass.Mapper.Sc.ISitecoreContext, how do we accomplish that):

var model = new Home_Control()
{ Top_Content = "Some Dummy Test Home Top Content" };
var iSitecoreContext = new Mock<Glass.Mapper.Sc.ISitecoreContext>();
//var iSitecoreContext = new Glass.Mapper.Sc.SitecoreContext();       
iSitecoreContext.Setup(_ => _.GetCurrentItem<Home_Control>(false, false)).Returns(model);
FakeHomeTopContentController controllerUnderTest = new FakeHomeTopContentController();
controllerUnderTest.FakeContext = (Glass.Mapper.Sc.SitecoreContext)iSitecoreContext;

If you don't want to create dependency injection through constructor you can do that through protected virtual method. Something like that:

public class HomeBottomContentController : GlassController
{
    public override ActionResult Index()
    {
        var context = this.GetContext();
        var model = context.GetCurrentItem<Home_Control>();
        return View("~/Views/HomeBottomContent/HomeBottomContent.cshtml", model);
    }

    protected virtual SitecoreContext GetContext() 
    {
        return new SitecoreContext();
    }
}

[TestClass]
public class MvcUnitTests
{
    [TestMethod]
    public void Test_HomeBottomContentController_With_ISitecoreContext()
    {     
        var controllerUnderTest = new FakeHomeBottomContentController();
        controllerUnderTest.FakeContext = /* set what you want */;
        var result = controllerUnderTest.Index() as ViewResult;
        Assert.IsNotNull(result);
        Assert.IsNotNull(result.Model);        
    }

    public class FakeHomeBottomContentController : HomeBottomContentController 
    {
        public SitecoreContext FakeContext { get; set; }

        protected override SitecoreContext GetContext()
        {
            return this.FakeContext;
        }
    }   
}

I don't know the limitations of your ability to end the source code but based on your comment to the other answer you have access to it. I would opt for Poor Man's DI, have two constructors on the controller. The unit test uses the second constructor and MVC will use the parameterless constructor.

public class HomeBottomContentController : GlassController
{

ISitecoreContext _iSitecoreContext;

public HomeBottomContentController() :this(new SitecoreContext()){
}


public HomeBottomContentController(ISitecoreContext iSitecoreContext)
{
    _iSitecoreContext = iSitecoreContext;
}



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

}

[TestClass]
public class MvcUnitTests
{
[TestMethod]
public void Test_HomeBottomContentController_With_ISitecoreContext()
{

    var model = new Home_Control()
    { Bottom_Content = "XYZ" };

    var iSitecoreContext = new Mock<Glass.Mapper.Sc.ISitecoreContext>();
    iSitecoreContext.Setup(_ => _.GetCurrentItem<Home_Control>(false, false)).Returns(model);

    HomeBottomContentController controllerUnderTest = new HomeBottomContentController(iSitecoreContext.Object);

    var result = controllerUnderTest.IndexTest() as ViewResult;

}

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM