在常规 mvc 控制器(不是 Web api 控制器)中使用身份的单元测试方法

问题描述 投票:0回答:3

我在常规 mvc 控制器中有一个方法,可以检查用户的身份以查看用户应该被定向到哪个页面。但是,针对此方法构建单元测试会引发“未设置对象引用”错误,因为当该方法作为测试的一部分运行时,User 始终为 null。

public ActionResult Index()
        {
            if (User.Identity.IsAuthenticated)
            {
                if (User.IsInRole("Administrator"))
                {
                    return RedirectToAction("Console");
                }

                ViewBag.StatusMessage = "You are logged in but do not have sufficient permissions.";
            }
            else
            {
                ViewBag.StatusMessage = "Please log in";
            }

            return View();
        }

我尝试了为在 ASP.NET Web API 中使用 User.Identity.Name 的方法编写单元测试中提供的各种解决方案。这些通常在单元测试的主体中使用如下内容:

var identity = new GenericIdentity("UserName");
Thread.CurrentPrincipal = new GenericPrincipal(identity, null);
var controller = new FooController(); 

如果控制器是 ApiController,即 WebApi 项目的控制器,则此方法工作正常,但它似乎不适用于常规 Web MVC 控制器。 User 对象保持为空。怎么解决?

c# asp.net-mvc unit-testing identity
3个回答
9
投票

您发布的与 WebAPI 相关的链接,可以理解,它非常容易对 ApiController 进行单元测试并访问 User 对象。

这与 ASP.NET MVC 有所不同,正如您所见,它并不简单。如果我们沿着使用手动/手写模拟的道路前进,我们可能会发现自己在嘲笑整个宇宙,而不是编写有价值的测试。

我将这两种方法都包含在内,以便您能够理解。我还使用问题中发布的链接中的相同示例。

使用手动手写模拟

//System Under Test - i.e to test User
public class SutController : Controller
{
    public string Get() {
        return User.Identity.Name;
    }
}

public class TestableControllerContext : ControllerContext {
    public TestableHttpContext TestableHttpContext { get; set; }
}

public class TestableHttpContext : HttpContextBase {
    public override IPrincipal User { get; set; }
}

[TestMethod]
public void IndexNoneMoq()
{
    var identity = new GenericIdentity("tugberk");
    var controller = new SutController();

    var controllerContext = new TestableControllerContext();
    var principal = new GenericPrincipal(identity, null);
    var testableHttpContext = new TestableHttpContext
    {
        User = principal
    };

    controllerContext.HttpContext = testableHttpContext;
    controller.ControllerContext = controllerContext;

    Assert.AreEqual(controller.Get(), identity.Name);
}

使用模拟/隔离框架,即 Moq

[TestMethod]
public void IndexMoq()
{
    var identity = new GenericIdentity("tugberk");          
    var controller = new SutController();

    var controllerContext = new Mock<ControllerContext>();
    var principal = new Mock<IPrincipal>();
    principal.Setup(p => p.IsInRole("Administrator")).Returns(true);
    principal.SetupGet(x => x.Identity.Name).Returns("tugberk");
    controllerContext.SetupGet(x => x.HttpContext.User).Returns(principal.Object);
    controller.ControllerContext = controllerContext.Object;

    Assert.AreEqual(controller.Get(), identity.Name);
}

请注意,我认为 ASP.NET 的下一版本对 WebAPI 和 MVC (AFAIK)使用相同的底层组件,因此这可能是使用 User 实例的单向单元测试目标控制器。


4
投票

这是您可以在 MVC 单元测试控制器中编写的手动模拟。

[TestMethod]
public void IndexManualMoq()
{
.
.
.
    var controller = new SutController();
    controller.ControllerContext = new ControllerContext
    {
        HttpContext = new MockHttpContext
        {
            User = new GenericPrincipal(new GenericIdentity("JDoe"), null)
        }
    };
.
.
.
}

private class MockHttpContext : HttpContextBase
{
    public override IPrincipal User { get; set; }
}

0
投票

ControllerTest.com 提供高精度测试服务,以验证控制器在各种应用中的功能和可靠性。在控制器测试探索他们的产品。

© www.soinside.com 2019 - 2024. All rights reserved.