【发布时间】:2023-03-19 19:30:01
【问题描述】:
如果用户已经登录 (User.Identity.IsAuthenticated),我有一个控制器操作会自动重定向到新页面。为这种情况编写单元测试以确保发生重定向的最佳方法是什么?
【问题讨论】:
标签: .net asp.net-mvc unit-testing
如果用户已经登录 (User.Identity.IsAuthenticated),我有一个控制器操作会自动重定向到新页面。为这种情况编写单元测试以确保发生重定向的最佳方法是什么?
【问题讨论】:
标签: .net asp.net-mvc unit-testing
我一直在使用以下 Moq 和 Moq,以便在我的单元测试中设置各种条件。一、HttpContextBase mock:
public static Mock<HttpContextBase> GetHttpContextMock(bool isLoggedIn)
{
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 principal = AuthenticationAndAuthorization.GetPrincipleMock(isLoggedIn);
context.SetupGet(c => c.Request).Returns(request.Object);
context.SetupGet(c => c.Response).Returns(response.Object);
context.SetupGet(c => c.Session).Returns(session.Object);
context.SetupGet(c => c.Server).Returns(server.Object);
context.SetupGet(c => c.User).Returns(principal.Object);
return context;
}
每个可能提供有用的 Mock 的属性都在这里设置。这样,如果我需要添加推荐人之类的东西,我可以使用:
Mock.Get(controller.Request).Setup(s => s.UrlReferrer).Returns(new Uri("http://blah.com/");
“GetPrincipleMock”方法是设置用户的方法。它看起来像这样:
public static Mock<IPrincipal> GetPrincipleMock(bool isLoggedIn)
{
var mock = new Mock<IPrincipal>();
mock.SetupGet(i => i.Identity).Returns(GetIdentityMock(isLoggedIn).Object);
mock.Setup(i => i.IsInRole(It.IsAny<string>())).Returns(false);
return mock;
}
public static Mock<IIdentity> GetIdentityMock(bool isLoggedIn)
{
var mock = new Mock<IIdentity>();
mock.SetupGet(i => i.AuthenticationType).Returns(isLoggedIn ? "Mock Identity" : null);
mock.SetupGet(i => i.IsAuthenticated).Returns(isLoggedIn);
mock.SetupGet(i => i.Name).Returns(isLoggedIn ? "testuser" : null);
return mock;
}
现在,我在测试中的控制器设置如下所示:
var controller = new ProductController();
var httpContext = GetHttpContextMock(true); //logged in, set to false to not be logged in
ControllerContext controllerContext = new ControllerContext(httpContext.Object, new RouteData(), controller);
controller.ControllerContext = controllerContext;
这是一个有点冗长的设置,但一旦一切就绪,测试各种条件就会变得容易得多。
【讨论】:
这不是最简单的事情,但可以做到。 User 属性只是委托给 Controller.HttpContext.User。两者都是非虚拟只读属性,因此您无法对它们做任何事情。但是,Controller.HttpContext 委托给 ControllerBase.ControllerContext,这是一个可写属性。
因此,您可以在执行被测系统 (SUT) 之前将 Test Double HttpContextBase 分配给 Controller.ControllerContext。使用起订量,它看起来像这样:
var user = new GenericPrincipal(new GenericIdentity(string.Empty), null);
var httpCtxStub = new Mock<HttpContextBase>();
httpCtxStub.SetupGet(ctx => ctx.User).Returns(user);
var controllerCtx = new ControllerContext();
controllerCtx.HttpContext = httpCtxStub.Object;
sut.ControllerContext = controllerCtx;
然后调用您的操作并验证返回结果是否为 RedirectResult。
此测试利用隐式知识,即当您创建一个空名称的 GenericIdentity 时,它会为 IsAuthenticated 返回 false。您可以考虑改用Mock<IIdentity> 使测试更明确。
【讨论】: