模拟ASP.NET MVC中的User.Identity

时间:2010-06-12 02:42:18

标签: asp.net asp.net-mvc mocking

我需要为ASP.NET MVC 2.0网站创建单元测试。该站点使用Windows身份验证。

我一直在阅读有必要为处理HttpContext的代码模拟HTTP上下文。我觉得我也开始处理DI模式了。 (为类提供IRepository类型的属性,然后在实例化控制器时传入Repository对象。)

然而,我不明白的是通过User.Identity模拟Windows Principal对象的正确方法。这是HttpContext的一部分吗?

是否有任何正文链接到展示此内容的文章(或推荐书籍)?

谢谢,

Trey Carroll

6 个答案:

答案 0 :(得分:31)

我已经使用IoC来取消一些成功。我首先定义了一个类来表示当前登录的用户:

public class CurrentUser
{
    public CurrentUser(IIdentity identity)
    {
        IsAuthenticated = identity.IsAuthenticated;
        DisplayName = identity.Name;

        var formsIdentity = identity as FormsIdentity;

        if (formsIdentity != null)
        {
            UserID = int.Parse(formsIdentity.Ticket.UserData);
        }
    }

    public string DisplayName { get; private set; }
    public bool IsAuthenticated { get; private set; }
    public int UserID { get; private set; }
}

构造函数中需要IIdentity来设置其值。对于单元测试,您可以添加另一个构造函数以允许您绕过IIdentity依赖项。

然后我使用Ninject(选择你最喜欢的IoC容器,没关系),并为IIdentity创建了一个绑定:

Bind<IIdentity>().ToMethod(c => HttpContext.Current.User.Identity);

然后,在我的控制器内部,我在构造函数中声明了依赖项:

CurrentUser _currentUser;

public HomeController(CurrentUser currentUser)
{
    _currentUser = currentUser;
}

IoC容器看到HomeController占用CurrentUser个对象,CurrentUser构造函数占用IIdentity。它将自动解决依赖关系,瞧!您的控制器可以知道当前登录用户是谁。对于FormsAuthentication,它似乎对我很有用。您可以将此示例调整为Windows身份验证。

答案 1 :(得分:18)

我不知道MVC 2.0,但在较新版本中你可以模拟ControllerContext:

// create mock principal
var mocks = new MockRepository(MockBehavior.Default);
Mock<IPrincipal> mockPrincipal = mocks.Create<IPrincipal>();
mockPrincipal.SetupGet(p => p.Identity.Name).Returns(userName);
mockPrincipal.Setup(p => p.IsInRole("User")).Returns(true);

// create mock controller context
var mockContext = new Mock<ControllerContext>();
mockContext.SetupGet(p => p.HttpContext.User).Returns(mockPrincipal.Object);
mockContext.SetupGet(p => p.HttpContext.Request.IsAuthenticated).Returns(true);

// create controller
var controller = new MvcController() { ControllerContext = mock.Object };

另见How to unit-test an MVC controller action which depends on authentification in c#?

答案 2 :(得分:10)

Scott Hanselman显示in his blog如何使用IPrincipal和ModelBinder通过模拟IPrincipal来更轻松地测试控制器。

答案 3 :(得分:5)

在MVC4上模拟用户名和SID的示例。 应测试以下操作中的用户名和SID(Windows身份验证):

[Authorize]
public class UserController : Controller
{
    public ActionResult Index()
    {
        // get Username
        ViewBag.Username = User.Identity.Name;

        // get SID
        var lIdentity = HttpContext.User.Identity as WindowsIdentity;
        ViewBag.Sid = lIdentity.User.ToString();

        return View();
    }
}

我使用MoqVisual Studio Test Tools。测试实施如下:

[TestMethod]
public void IndexTest()
{
    // Arrange
    var myController = new UserController();
    var contextMock = new Mock<ControllerContext>();
    var httpContextMock = new Mock<HttpContextBase>();
    var lWindowsIdentity = new WindowsIdentity("Administrator");

    httpContextMock.Setup(x => x.User).Returns(new WindowsPrincipal(lWindowsIdentity));

    contextMock.Setup(ctx => ctx.HttpContext).Returns(httpContextMock.Object);
    myController.ControllerContext = contextMock.Object;

    // Act
    var lResult = myController.Index() as ViewResult;

    // Assert
    Assert.IsTrue(lResult.ViewBag.Username == "Administrator");
    Assert.IsTrue(lResult.ViewBag.Sid == "Any SID Pattern");
}

答案 4 :(得分:1)

我更改了dev环境global.asax和Web.Config,以便使用FormsAuth来强制特定用户。用户名使用相同的WindowsAuth格式。参见:

public override void Init()
    {
        base.Init();

        this.PostAuthenticateRequest += 
             new EventHandler(MvcApplication_PostAuthenticateRequest);
    }

    void MvcApplication_PostAuthenticateRequest(object sender, EventArgs e)
    {
        FormsAuthentication.SetAuthCookie("Domain\\login", true);
    }

Windows或Forms Auth共享相同的登录模式

该应用程序将同时使用Windows身份验证和表单身份验证。

答案 5 :(得分:0)

要模拟WindowsIdentity,您可以执行以下操作:

var mockedPrincipal = new Mock<WindowsPrincipal>(WindowsIdentity.GetCurrent());

mockedPrincipal.SetupGet(x => x.Identity.IsAuthenticated).Returns(true);
mockedPrincipal.SetupGet(x => x.Identity.Name).Returns("Domain\\User1");
mockedPrincipal.Setup(x => x.IsInRole("Domain\\Group1")).Returns(true);
mockedPrincipal.Setup(x => x.IsInRole("Domain\\Group2")).Returns(false);

然后使用mockedPrincipal.Object获取实际的WindowsIdentity