Asp.net mvc 3 Moq正在显示404错误
我有以下404错误ActionResult,当找不到网页时会抛出:Asp.net mvc 3 Moq正在显示404错误,asp.net-mvc-3,unit-testing,nunit,moq,http-status-code-404,Asp.net Mvc 3,Unit Testing,Nunit,Moq,Http Status Code 404,我有以下404错误ActionResult,当找不到网页时会抛出: public ActionResult InvokeHttp404(HttpContextBase httpContext) { IController errorController = new ErrorController(); var errorRoute = new RouteData(); errorRoute.Values.Add("controller", "Error");
public ActionResult InvokeHttp404(HttpContextBase httpContext) {
IController errorController = new ErrorController();
var errorRoute = new RouteData();
errorRoute.Values.Add("controller", "Error");
errorRoute.Values.Add("action", "Http404");
errorRoute.Values.Add("url", httpContext.Request.Url.OriginalString);
errorController.Execute(new RequestContext(httpContext, errorRoute));
return new EmptyResult();
}
我试图通过以下测试对其进行单元测试:
[TestMethod]
public void Details_Get_404Handler()
{
// Arrange
var controller = GetController(new Repository(), FakeHttpContext());
// Act
var result = controller.Details(3442399) as ViewResult; // invalid Id (not found)
//Assert
Assert.AreEqual("NotFound", result.ViewName);
}
我已经在这个测试上呆了很长时间了,它会在请求Url.OriginalString
的代码行上抛出一个null异常。在阅读了之前的文章后,我发现第二个答案非常有帮助,如下所示:(我添加了一行内容来处理Url字符串)
根据Marnix的回答修改代码:
public static HttpContextBase FakeHttpContext()
{
var context = new Mock<HttpContextBase>();
var request = new Mock<HttpRequestBase>();
var response = new Mock<HttpResponseBase>();
var session = new Mock<HttpSessionStateBase>();
var server = new Mock<HttpServerUtilityBase>();
var user = new Mock<IPrincipal>();
var identity = new Mock<IIdentity>();
var files = new Mock<HttpFileCollectionBase>();
request.SetupGet(x => x.Url).Returns(new Uri("http://localhost/a", UriKind.Absolute));
request.Setup(req => req.ApplicationPath).Returns("~/");
request.Setup(req => req.AppRelativeCurrentExecutionFilePath).Returns("~/");
request.Setup(req => req.PathInfo).Returns(string.Empty);
request.Setup(req => req.ContentType).Returns("text/html");
request.Setup(req => req.QueryString).Returns(new NameValueCollection());
request.Setup(req => req.Form).Returns(new NameValueCollection());
request.Setup(req => req.Files).Returns(files.Object);
response.Setup(res => res.ApplyAppPathModifier(It.IsAny<string>()))
.Returns((string virtualPath) => virtualPath);
user.Setup(usr => usr.Identity).Returns(identity.Object);
identity.SetupGet(ident => ident.IsAuthenticated).Returns(true);
context.Setup(ctx => ctx.Request).Returns(request.Object);
context.Setup(ctx => ctx.Response).Returns(response.Object);
context.Setup(ctx => ctx.Session).Returns(session.Object);
context.Setup(ctx => ctx.Server).Returns(server.Object);
context.Setup(ctx => ctx.User).Returns(user.Object);
ValueProviderFactories.Factories
.ReplaceNameValueCollectionWith<FormValueProviderFactory>(ctx => ctx.HttpContext.Request.Form)
.ReplaceNameValueCollectionWith<QueryStringValueProviderFactory>(ctx => ctx.HttpContext.Request.QueryString)
.ReplaceHttpFileCollectionWith<HttpFileCollectionValueProviderFactory>(ctx => ctx.HttpContext.Request.Files);
return context.Object;
}
模拟控制器的上下文有点棘手。在MVC3中,还有一个额外的步骤来防止值提供程序接触HttpContext.Current 解释你需要做什么 更新:您可能不会直接使用ValueProviderFactorys,但控制器上的ActionInvoker会直接使用。在深层的某个地方,值提供程序的默认实现访问HttpContext.Current,HttpContext.Current不是在web请求之外设置的,在单元测试期间会导致NullReferenceException。您可以通过替换默认值提供程序(如链接答案中所述)来防止这种情况 将上面链接中的
valueProviderFactoryExtensions
类复制到您的测试项目中,并将以下代码添加到您的FakeHttpContext
方法中:
var form = new NameValueCollection();
var queryString = new NameValueCollection();
request.Setup( x => x.Form ).Returns( form );
request.Setup( x => x.QueryString ).Returns( queryString );
ValueProviderFactories.Factories
.ReplaceWith<FormValueProviderFactory>(ctx => form)
.ReplaceWith<QueryStringValueProviderFactory>(ctx => queryString);
var form=new NameValueCollection();
var queryString=new NameValueCollection();
request.Setup(x=>x.Form).Returns(Form);
request.Setup(x=>x.QueryString).Returns(QueryString);
价值提供者工厂。工厂
.ReplaceWith(ctx=>form)
.replace为(ctx=>queryString);
我有一个像问题中那个家伙一样的错误控制器,但我要问的是如何使用MOQ对404错误控制器进行单元测试。以前没有使用ValueProviderFactorys,考虑到您所指的问题中有ValueProviderFactoryExtensions类,我可以从测试方法中显示如何调用它们吗?如果有人有解决方案,我仍然希望在Moq中看到解决方案…我更新了我的答案,解释了如何以及为什么需要调整ValueProviderFactorys。测试结果详细信息中的堆栈跟踪现在已经超过了namevaluecollections,但现在似乎无法找到viewname?我已经发布了我在我的问题中使用的更新代码-它和你说的不完全一样,但有相同的区别,对吗?看起来差不多。我的最佳猜测是,您的错误控制器正在尝试返回视图结果。调用Execute
时,将执行整个管道,包括视图。由于您正在进行单元测试,ViewEngine很可能无法找到视图。你有两个选择;终止视图引擎或重新考虑实现控制器的方式。例如,您可以抛出错误代码为404的HttpException,并使用自定义HandleErrorAttribute实现处理该异常。这将使您的控制器更易于测试。
public static HttpContextBase FakeHttpContext()
{
var context = new Mock<HttpContextBase>();
var request = new Mock<HttpRequestBase>();
var response = new Mock<HttpResponseBase>();
var session = new Mock<HttpSessionStateBase>();
var server = new Mock<HttpServerUtilityBase>();
var user = new Mock<IPrincipal>();
var identity = new Mock<IIdentity>();
var files = new Mock<HttpFileCollectionBase>();
request.SetupGet(x => x.Url).Returns(new Uri("http://localhost/a", UriKind.Absolute));
request.Setup(req => req.ApplicationPath).Returns("~/");
request.Setup(req => req.AppRelativeCurrentExecutionFilePath).Returns("~/");
request.Setup(req => req.PathInfo).Returns(string.Empty);
request.Setup(req => req.ContentType).Returns("text/html");
request.Setup(req => req.QueryString).Returns(new NameValueCollection());
request.Setup(req => req.Form).Returns(new NameValueCollection());
request.Setup(req => req.Files).Returns(files.Object);
response.Setup(res => res.ApplyAppPathModifier(It.IsAny<string>()))
.Returns((string virtualPath) => virtualPath);
user.Setup(usr => usr.Identity).Returns(identity.Object);
identity.SetupGet(ident => ident.IsAuthenticated).Returns(true);
context.Setup(ctx => ctx.Request).Returns(request.Object);
context.Setup(ctx => ctx.Response).Returns(response.Object);
context.Setup(ctx => ctx.Session).Returns(session.Object);
context.Setup(ctx => ctx.Server).Returns(server.Object);
context.Setup(ctx => ctx.User).Returns(user.Object);
ValueProviderFactories.Factories
.ReplaceNameValueCollectionWith<FormValueProviderFactory>(ctx => ctx.HttpContext.Request.Form)
.ReplaceNameValueCollectionWith<QueryStringValueProviderFactory>(ctx => ctx.HttpContext.Request.QueryString)
.ReplaceHttpFileCollectionWith<HttpFileCollectionValueProviderFactory>(ctx => ctx.HttpContext.Request.Files);
return context.Object;
}
System.Web.Mvc.ViewResult.FindView(ControllerContext context)
var form = new NameValueCollection();
var queryString = new NameValueCollection();
request.Setup( x => x.Form ).Returns( form );
request.Setup( x => x.QueryString ).Returns( queryString );
ValueProviderFactories.Factories
.ReplaceWith<FormValueProviderFactory>(ctx => form)
.ReplaceWith<QueryStringValueProviderFactory>(ctx => queryString);