Mock HttpContext.Current in Test Init Method
我正在尝试将单元测试添加到我构建的ASP.NET MVC应用程序中。 在我的单元测试中,我使用以下代码:
1 2 3 4 5 6 7 | [TestMethod] public void IndexAction_Should_Return_View() { var controller = new MembershipController(); controller.SetFakeControllerContext("TestUser"); ... } |
使用以下帮助程序来模拟控制器上下文:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 | public static class FakeControllerContext { public static HttpContextBase FakeHttpContext(string username) { var context = new Mock<HttpContextBase>(); context.SetupGet(ctx => ctx.Request.IsAuthenticated).Returns(!string.IsNullOrEmpty(username)); if (!string.IsNullOrEmpty(username)) context.SetupGet(ctx => ctx.User.Identity).Returns(FakeIdentity.CreateIdentity(username)); return context.Object; } public static void SetFakeControllerContext(this Controller controller, string username = null) { var httpContext = FakeHttpContext(username); var context = new ControllerContext(new RequestContext(httpContext, new RouteData()), controller); controller.ControllerContext = context; } } |
此测试类继承自具有以下内容的基类:
1 2 3 4 | [TestInitialize] public void Init() { ... } |
在这个方法里面它调用一个库(我无法控制),它试图运行以下代码:
1 | HttpContext.Current.User.Identity.IsAuthenticated |
现在您可以看到问题所在。 我已经针对控制器设置了假的HttpContext,但是没有在这个基本的Init方法中。 单元测试/嘲笑对我来说很新,所以我想确保我做对了。 对我来说,模拟HttpContext的正确方法是什么,以便在我的控制器和我的Init方法中调用的任何库之间共享它。
幸运的是,
即使在控制台应用程序中,以下代码也按预期运行:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 | HttpContext.Current = new HttpContext( new HttpRequest("","http://tempuri.org",""), new HttpResponse(new StringWriter()) ); // User is logged in HttpContext.Current.User = new GenericPrincipal( new GenericIdentity("username"), new string[0] ); // User is logged out HttpContext.Current.User = new GenericPrincipal( new GenericIdentity(String.Empty), new string[0] ); |
Test Init也将完成这项工作。
1 2 3 4 5 6 |
我知道这是一个较旧的主题,但是我们定期对Mocking MVC应用程序进行单元测试。
我只想添加我的经验在升级到Visual Studio 2013之后使用Moq 4模拟MVC 3应用程序。没有任何单元测试在调试模式下工作,并且当试图查看变量时HttpContext显示"无法计算表达式" 。
原来,visual studio 2013在评估某些对象方面存在问题。为了让调试模拟的Web应用程序再次运行,我必须检查Tools => Options => Debugging => General settings中的"Use Managed Compatibility Mode"。
我通常做这样的事情:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 | public static class FakeHttpContext { public static void SetFakeContext(this Controller controller) { var httpContext = MakeFakeContext(); ControllerContext context = new ControllerContext( new RequestContext(httpContext, new RouteData()), controller); controller.ControllerContext = context; } private static HttpContextBase MakeFakeContext() { 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>(); context.Setup(c=> c.Request).Returns(request.Object); context.Setup(c=> c.Response).Returns(response.Object); context.Setup(c=> c.Session).Returns(session.Object); context.Setup(c=> c.Server).Returns(server.Object); context.Setup(c=> c.User).Returns(user.Object); user.Setup(c=> c.Identity).Returns(identity.Object); identity.Setup(i => i.IsAuthenticated).Returns(true); identity.Setup(i => i.Name).Returns("admin"); return context.Object; } } |
并启动这样的上下文
1 | FakeHttpContext.SetFakeContext(moController); |
并直接在控制器中调用Method
1 2 | long lReportStatusID = -1; var result = moController.CancelReport(lReportStatusID); |
如果您的应用程序第三方在内部重定向,那么最好以下面的方式模拟HttpContext:
1 2 3 4 | HttpWorkerRequest initWorkerRequest = new SimpleWorkerRequest("","","","",new StringWriter(CultureInfo.InvariantCulture)); System.Web.HttpContext.Current = new HttpContext(initWorkerRequest); System.Web.HttpContext.Current.Request.Browser = new HttpBrowserCapabilities(); System.Web.HttpContext.Current.Request.Browser.Capabilities = new Dictionary<string, string> { {"requiresPostRedirectionHandling","false" } }; |