如何对使用 Sitecore.Mvc.Presentation.RenderingContext 的 GlassController 操作进行单元测试
How to Unit Test a GlassController Action which Uses Sitecore.Mvc.Presentation.RenderingContext
我是一名 sitecore 开发人员,我想创建一个示例 sitecore helix 单元测试项目来测试您在我们的 "EmailArticleController" 控制器的 Index() 操作方法中看到的逻辑:
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);
}
}
好的,这就是我的单元测试 class:
[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);
}
}
基本上我想模拟渲染上下文,确保它有一个(字符串)DataSource 值设置为某个值,例如“/sitecore/home/...”,我想将它发送到控制器的构造函数(如果这是正确的方法),调用 Index(int ) 方法,同时确保我的 _businessLogic,在这种情况下它只是一个接口(它应该是具体的 class 吗?)它的数据源在返回视图之前设置为相同的值。
执行所有这些操作的确切代码是什么?谢谢!
将您的代码与 RenderingContext.Current.Rendering.DataSource
等静态依赖项紧密耦合会使单独测试代码变得困难。
我建议您创建一个包装器来封装对 RenderingContext
的静态访问。参考 GitHub
上 Glass.Mapper 存储库中的代码示例
public interface IRenderingContext {
string GetDataSource();
}
//...
using Sitecore.Mvc.Presentation;
public class RenderingContextWrapper : IRenderingContext {
public string GetDataSource(){
return RenderingContext.CurrentOrNull.Rendering.DataSource;
}
}
然后您将更新您的控制器以通过构造函数注入显式依赖于该抽象
public class EmailArticleController : GlassController {
private readonly IEmailArticleBusiness businessLogic;
private readonly IRenderingContext renderingContext;
public EmailArticleController(IEmailArticleBusiness businessLogic, IRenderingContext renderingContext) {
this.businessLogic = businessLogic;
this.renderingContext = renderingContext;
}
public ActionResult Index() {
var model = businessLogic.FetchPopulatedModel;
var datasourceId = renderingContext.GetDataSource();
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);
var datasourceId = "fake_datasourceId";
var renderingContext = Mock.Of<IRenderingContext>(_ => _.GetDataSource() == datasourceId);
var controllerUnderTest = new EmailArticleController(businessLogicFake.Object, renderingContext);
//Act
var result = controllerUnderTest.Index() as ViewResult;
//Assert
Assert.IsNotNull(result);
businessLogicFake.Verify(_ => _.SetDataSourceID(datasourceId), Times.AtLeastOnce());
}
}
您的生产代码显然会向 DI 容器注册抽象和实现,以在运行时解析依赖项。
我是一名 sitecore 开发人员,我想创建一个示例 sitecore helix 单元测试项目来测试您在我们的 "EmailArticleController" 控制器的 Index() 操作方法中看到的逻辑:
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);
}
}
好的,这就是我的单元测试 class:
[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);
}
}
基本上我想模拟渲染上下文,确保它有一个(字符串)DataSource 值设置为某个值,例如“/sitecore/home/...”,我想将它发送到控制器的构造函数(如果这是正确的方法),调用 Index(int ) 方法,同时确保我的 _businessLogic,在这种情况下它只是一个接口(它应该是具体的 class 吗?)它的数据源在返回视图之前设置为相同的值。
执行所有这些操作的确切代码是什么?谢谢!
将您的代码与 RenderingContext.Current.Rendering.DataSource
等静态依赖项紧密耦合会使单独测试代码变得困难。
我建议您创建一个包装器来封装对 RenderingContext
的静态访问。参考 GitHub
public interface IRenderingContext {
string GetDataSource();
}
//...
using Sitecore.Mvc.Presentation;
public class RenderingContextWrapper : IRenderingContext {
public string GetDataSource(){
return RenderingContext.CurrentOrNull.Rendering.DataSource;
}
}
然后您将更新您的控制器以通过构造函数注入显式依赖于该抽象
public class EmailArticleController : GlassController {
private readonly IEmailArticleBusiness businessLogic;
private readonly IRenderingContext renderingContext;
public EmailArticleController(IEmailArticleBusiness businessLogic, IRenderingContext renderingContext) {
this.businessLogic = businessLogic;
this.renderingContext = renderingContext;
}
public ActionResult Index() {
var model = businessLogic.FetchPopulatedModel;
var datasourceId = renderingContext.GetDataSource();
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);
var datasourceId = "fake_datasourceId";
var renderingContext = Mock.Of<IRenderingContext>(_ => _.GetDataSource() == datasourceId);
var controllerUnderTest = new EmailArticleController(businessLogicFake.Object, renderingContext);
//Act
var result = controllerUnderTest.Index() as ViewResult;
//Assert
Assert.IsNotNull(result);
businessLogicFake.Verify(_ => _.SetDataSourceID(datasourceId), Times.AtLeastOnce());
}
}
您的生产代码显然会向 DI 容器注册抽象和实现,以在运行时解析依赖项。