我正在为 ASP.NET MVC 4 编写一个自定义 RazorViewEngine 并且我很难对它进行单元测试,因为基类正在调用向上到 BuildManager 和 VirtualPathProvider 的东西,它们会抛出各种异常,所以在我进行的每一个新测试中,我都需要存根其他东西,因为底层机制正在调用我没有存根的对象并抛出“对象引用未设置为对象的实例”。
所以我所做的是编写一个与此类似的接口,它允许我包装对引擎基类的调用。
internal interface IViewEngineDelegate
{
Func<ControllerContext, string, IView> CreatePartialView { get; set; }
Func<ControllerContext, string, string, IView> CreateView { get; set; }
Func<ControllerContext, string, bool> FileExists { get; set; }
Func<ControllerContext, string, bool, ViewEngineResult> FindPartialView { get; set; }
Func<ControllerContext, string, string, bool, ViewEngineResult> FindView { get; set; }
}
现在,我可以在我的生产代码中执行以下操作。
public class CsEmbeddedRazorViewEngine : RazorViewEngine
{
private readonly IViewEngineDelegate _viewEngineDelegate;
public CsEmbeddedRazorViewEngine()
{
_viewEngineDelegate = new ViewEngineDelegate
{
CreatePartialView = base.CreatePartialView,
CreateView = base.CreateView,
FileExists = base.FileExists,
FindPartialView = base.FindPartialView,
FindView = base.FindView
};
}
internal CsEmbeddedRazorViewEngine(IViewEngineDelegate viewEngineDelegate)
{
_viewEngineDelegate = viewEngineDelegate;
}
protected override IView CreatePartialView(ControllerContext controllerContext, string partialPath)
{
// TODO: Do something.
return _viewEngineDelegate.CreatePartialView(controllerContext, partialPath)
}
}
最后,我可以通过 stubing 调用来测试它,就像这样。
ViewEngineDelegate engineDelegate = new ViewEngineDelegate
{
CreatePartialView = (controllerContext, partialPath) => FakeViewFactory.Instance.Create(controllerContext, partialPath),
};
CsEmbeddedRazorViewEngine engine = new CsEmbeddedRazorViewEngine(engineDelegate);
经过一番思考,我想到了这样做,因为我认为我过度设计了设计,所以我决定采用更直接的方法。
public class CsEmbeddedRazorViewEngine : RazorViewEngine
{
protected sealed override IView CreatePartialView(ControllerContext controllerContext, string partialPath)
{
// TODO: Do something.
return default(IView);
}
protected virtual IView CreatePartialViewCore(ControllerContext controllerContext, string partialPath)
{
return base.CreatePartialView(controllerContext, partialPath);
}
}
我对这些方法中的任何一种都不太满意,这就是我发布它的原因,我想知道是否有更好的方法来做到这一点或更好,也许只有我,这些都是可以接受/合理的方法。