在常规mvc控制器中使用标识的单元测试方法(不是web api控制器)

jul*_*ian 6 c# asp.net-mvc unit-testing identity

我在常规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();
        }
Run Code Online (Sandbox Code Playgroud)

我已经尝试过在Writing Unit Test中为ASP.NET Web API中使用User.Identity.Name的方法提供的各种解决方案.这些通常在单元测试的主体内使用类似下面的内容:

var identity = new GenericIdentity("UserName");
Thread.CurrentPrincipal = new GenericPrincipal(identity, null);
var controller = new FooController(); 
Run Code Online (Sandbox Code Playgroud)

如果Controller是ApiController,这种方法可以正常工作 - 即WebApi项目的控制器,但它似乎不适用于常规的Web mvc控制器.User对象保持为null.怎么解决?

Spo*_*ock 7

您发布的此链接与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);
}
Run Code Online (Sandbox Code Playgroud)

使用Mocking/Isolation框架即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);
}
Run Code Online (Sandbox Code Playgroud)

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