我是一名sitecore开发人员,我想创建一个示例sitecore helix单元测试项目,用于测试您在我们的“EmailArticleController”控制器中看到的逻辑指数()操作方法:如何单元测试使用Sitecore.Mvc.Presentation.RenderingContext的GlassController动作
using Sitecore.Mvc.Presentation;
public class EmailArticleController : GlassController
{
//logic in below Index() method is what I want to test
public override ActionResult Index()
{
var _emailArticleBusiness = new EmailArticleBusiness();
var model = _emailArticleBusiness.FetchPopulatedModel;
var datasourceId = RenderingContext.Current.Rendering.DataSource;
_emailArticleBusiness.SetDataSourceID(datasourceId);
return View("~/Views/EmailCampaign/EmailArticle.cshtml", model);
}
//below is alternative code I wrote for mocking and unit testing the logic in above Index() function
private readonly IEmailArticleBusiness _businessLogic;
private readonly RenderingContext _renderingContext;
public EmailArticleController(IEmailArticleBusiness businessLogic, RenderingContext renderingContext)
{
_businessLogic = businessLogic;
_renderingContext = renderingContext;
}
public ActionResult Index(int forUnitTesting)
{
var model = _businessLogic.FetchPopulatedModel;
// *** do below two lines of logic somehow go into my Unit Testing class? How?
var datasourceId = _renderingContext.Rendering.DataSource;
_businessLogic.SetDataSourceID(datasourceId);
// ***
return View("~/Views/EmailCampaign/EmailArticle.cshtml", model);
}
}
好了,这是我在我的单元测试类:
[TestClass]
public class UnitTest1
{
[TestMethod]
public void Test_EmailArticleController_With_RenderingContext()
{
//Arrange
var businessLogicFake = new Mock<IEmailArticleBusiness>();
var model = new EmailArticleViewModel()
{
ArticleControl = new Article_Control() { },
Metadata = new Metadata() { }
};
businessLogicFake.Setup(x => x.FetchPopulatedModel).Returns(model);
// I'm not sure about the next 3 lines, how do I mock the RenderingContext and send it into the constructor, given that it needs a DataSource value too?
var renderingContext = Mock.Of<Sitecore.Mvc.Presentation.RenderingContext>(/*what goes here, if anything?*/) { /*what goes here, if anything?*/ };
EmailArticleController controllerUnderTest = new EmailArticleController(businessLogicFake.Object, renderingContext);
var result = controllerUnderTest.Index(3) as ViewResult;
Assert.IsNotNull(result);
}
}
基本上我想嘲笑渲染上下文,确保它有一个(字符串)数据源值设置为某些值,如“/ sitecore/home/...”,I w ant将它发送到控制器的构造函数中(如果这是正确的方式),调用Index(int)方法,同时确保我的_businessLogic,在这种情况下它只是一个接口(它应该是具体的类?)在返回视图之前将其dataSource设置为相同的值。
做所有这些的确切代码是什么?谢谢!
非常感谢你的回答!它像一个魅力工作!你说过:“将代码紧密结合到像RenderingContext.Current.Rendering.DataSource这样的静态依赖关系可以使测试代码变得困难,最好创建一个封装来封装对RenderingContext的静态访问。” 为什么我们绝对必须向EmailArticleController()类添加代码才能进行单元测试,还有其他原因吗?你暗示我们不可能在没有向构造函数中发送任何东西的情况下对原始的Index()方法进行单元测试,对吗? – user3034243
@ user3034243它更多的是设计问题。您无法控制该静态依赖关系,这意味着您无法控制在正常操作之外如何进行初始化。缺乏对不拥有的代码的控制,使得单独测试变得困难。阅读SOLID这样的主题,您将会更好地理解它与设计易于维护的清洁代码之间的关系,其中还包括测试。 – Nkosi
@ user3034243我几乎可以肯定,可能有另一种可能的方法,但是当你可以轻松地设计它时,我会每次选择更清洁的设计。 – Nkosi