如何对 GlassController 操作进行单元测试,其中 Returns 视图采用模型

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

我是一名 sitecore 开发人员,我想创建一个示例 sitecore helix 单元测试项目来测试我们的 "HomeBottomContentController" 控制器:

    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);
        }
    }

我创建了一个 WTW.Feature.HomeBottomContent.Tests 项目,目的是使用 helix 单元测试来测试整个组件。在其中我有一个 UnitTest1.cs 文件,其中包含以下内容:

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);
        }
    }
}

这个测试确实通过了,这意味着 "result" 不是空的;但是,问题是当我进入 Index() 代码时,我看到 "model" 变量在我们执行

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

我的问题是,我究竟该如何更改此代码以确保该行中的 "model" 不会变为空值?我如何 "mock" _iSitecoreContext 的单元测试代码中的项目,以便它有一个 "Home_Control" 模板,其字段具有合法值?这甚至是正确的方法吗?我发现的大多数在线资源都没有类似的情况,我正在寻找可能的最短代码。

我遇到的另一个问题是,假设 SitecoreContext 是在 inside Index() 方法中声明的,我如何在我的 [TestMethod] 中测试下面的 Index() 方法,而不是像上面那样在 HomeBottomContentController 构造函数中接收?有没有办法从 [TestMethod] 执行此操作,或者我们必须将 SitecoreContext 作为参数发送到 HomeBottomContentController 构造函数或 Index() 方法中?

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

在这种情况下,您需要在模拟的依赖项上模拟所需的行为

[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.
    }
}

更新

在动作中创建上下文会将其与上下文紧密耦合,使其几乎不可能被模拟。这就是注入显式依赖项的原因

你可以这样做:

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();
        }
    }
}