【问题标题】:How do I unit test a controller action that uses ther Controller.User variable?如何对使用 Controller.User 变量的控制器操作进行单元测试?
【发布时间】:2023-03-19 19:30:01
【问题描述】:

如果用户已经登录 (User.Identity.IsAuthenticated),我有一个控制器操作会自动重定向到新页面。为这种情况编写单元测试以确保发生重定向的最佳方法是什么?

【问题讨论】:

    标签: .net asp.net-mvc unit-testing


    【解决方案1】:

    我一直在使用以下 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;
    

    这是一个有点冗长的设置,但一旦一切就绪,测试各种条件就会变得容易得多。

    【讨论】:

    • 完美。答案仍然相关。+1
    【解决方案2】:

    这不是最简单的事情,但可以做到。 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&lt;IIdentity&gt; 使测试更明确。

    【讨论】:

      猜你喜欢
      • 2020-09-05
      • 1970-01-01
      • 1970-01-01
      • 2016-02-12
      • 2011-11-23
      • 2012-01-20
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多