我正在尝试对将数据填充到ViewData中的控制器进行单元测试。我们所有的观点都需要类似的数据(客户信息来自网址)。因此,原始开发人员选择将此ViewData填充放入OnActionExecuting事件中,而不是将调用放入每个控制器方法中。
当然,当您从单元测试中调用控制器的操作时,OnActionExecuting不会触发。 (感谢MVC团队!)
所以我尝试创建一个自定义视图引擎,并在请求视图时将客户数据填充到controllerContext中。这在浏览器中工作正常,但是当我运行此测试时,我的viewEngine会被忽略。没有任何数量的ViewEngines.Add(新的funkyViewEngine)有任何影响。
[TestMethod()]
public void LoginTest()
{
ViewEngines.Engines.Clear();
ViewEngines.Engines.Add(new FunkyViewEngine());
UserController target = new UserController();
target.SetStructureMap(); <--sets us to use the test repo
target.ControllerContext.HttpContext = MVCHelpers.FakeHttpContext("https://customersubdomain.ourdomain.com"); <--moq magic
var actual = target.Login();
Assert.IsTrue(actual.GetType().IsAssignableFrom(typeof(System.Web.Mvc.ViewResult)));
var vr = actual as ViewResult;
Assert.IsTrue(vr.ViewData.Community() != null); <--"Community" should be set by viewengine
Assert.IsTrue(vr.ViewData.Community().Subdomain == "customersubdomain.ourdomain");
Assert.IsTrue(vr.ViewData.Community().CanRegister);
}
这里有什么希望吗?我如何1)创建一个在浏览器和单元框架中调用控制器执行的方法或2)获取单元框架来调用我的视图引擎。
答案 0 :(得分:2)
对不起你的沮丧。当您从单元测试直接调用您的操作方法时,您看到OnActionExecuting
未被调用的原因是因为这不是MVC中的工作方式。
请求通过“管道”执行,就该区域而言,该管道由ControllerActionInvoker
组成。本课程负责:
OnActionExecuting
方法(注意:您的控制器类也是操作过滤器)OnActionExecuted
方法在您的单元测试中,您直接调用步骤3.并跳过所有其他步骤。在单元测试中,您有责任调用您的操作所需的任何设置代码。
但是,这并不意味着您现在应该编写使用ControllerActionInvoker
来执行整个管道的单元测试。我们(MVC团队)已经确认所有部分都在一起工作。
相反,您应该测试您的特定应用程序代码。在这种情况下,您可以考虑进行以下单元测试:
OnActionExecuting
是否将正确的Customer对象放入ViewData 我的最后一点是你应该将功能保留在OnActionExecuting
中。自定义视图引擎定义为错误的位置。
答案 1 :(得分:0)
不是您可能正在寻找的答案,但我使用自定义 MvcHandler 来实现相同的目标(在多租户应用中从URL获取客户)。 ViewEngine对我来说听起来不是这种逻辑的好地方......
我的自定义处理程序或多或少看起来像这样:
public class AccountMvcHandler : MvcHandler
{
public Account Account { get; private set; }
protected override IAsyncResult BeginProcessRequest(HttpContext httpContext, AsyncCallback callback, object state)
{
return base.BeginProcessRequest(httpContext, callback, state);
}
protected override IAsyncResult BeginProcessRequest(HttpContextBase httpContext, AsyncCallback callback, object state)
{
string accountName = this.RequestContext.RouteData.GetRequiredString("account");
Account = ServiceFactory.GetService<ISecurityService>().GetAccount(accountName);
return base.BeginProcessRequest(httpContext, callback, state);
}
}