简体   繁体   English

模拟ASP.NET MVC中的User.Identity

[英]Mocking User.Identity in ASP.NET MVC

I need to create Unit Tests for an ASP.NET MVC 2.0 web site. 我需要为ASP.NET MVC 2.0网站创建单元测试。 The site uses Windows Authentication. 该站点使用Windows身份验证。

I've been reading up on the necessity to mock the HTTP context for code that deals with the HttpContext. 我一直在阅读有必要为处理HttpContext的代码模拟HTTP上下文。 I feel like I'm starting to get a handle on the DI pattern as well. 我觉得我也开始处理DI模式了。 (Give the class an attribute of type IRepository and then pass in a Repository object when you instantiate the controller.) (为类提供类型为IRepository的属性,然后在实例化控制器时传入Repository对象。)

What I don't understand, however, is the proper way to Mock the Windows Principal object available through User.Identity. 但是,我不明白的是通过User.Identity模拟Windows Principal对象的正确方法。 Is this part of the HttpContext? 这是HttpContext的一部分吗?

Does any body have a link to an article that demonstrates this (or a recommendation for a book)? 是否有任何正文链接到展示此内容的文章(或推荐书籍)?

Thanks, 谢谢,

Trey Carroll 特雷卡罗尔

I've used IoC to abstract this away with some success. 我已经使用IoC来取消一些成功。 I first defined a class to represent the currently logged in user: 我首先定义了一个类来表示当前登录的用户:

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; }
}

It takes an IIdentity in the constructor to set its values. 它需要构造函数中的IIdentity来设置其值。 For unit tests, you could add another constructor to allow you bypass the IIdentity dependency. 对于单元测试,您可以添加另一个构造函数以允许您绕过IIdentity依赖项。

And then I use Ninject (pick your favorite IoC container, doesn't matter), and created a binding for IIdentity as such: 然后我使用Ninject(选择你最喜欢的IoC容器,没关系),并为IIdentity创建了一个绑定:

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

Then, inside of my controller I declare the dependency in the constructor: 然后,在我的控制器内部,我在构造函数中声明了依赖项:

CurrentUser _currentUser;

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

The IoC container sees that HomeController takes a CurrentUser object, and the CurrentUser constructor takes an IIdentity . IoC容器看到HomeController接受一个CurrentUser对象,而CurrentUser构造函数接受一个IIdentity It will resolve the dependencies automatically, and voila! 它将自动解决依赖关系,瞧! Your controller can know who the currently logged on user is. 您的控制器可以知道当前登录用户是谁。 It seems to work pretty well for me with FormsAuthentication. 对于FormsAuthentication,它似乎对我很有用。 You might be able to adapt this example to Windows Authentication. 您可以将此示例调整为Windows身份验证。

I don't know for MVC 2.0, but in newer versions you can mock the ControllerContext: 我不知道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 };

see also How to unit-test an MVC controller action which depends on authentification in c#? 另请参阅如何对依赖于c#中的身份验证的MVC控制器操作进行单元测试?

Scott Hanselman 在他的博客中展示如何使用IPrincipal和ModelBinder通过模拟IPrincipal来更轻松地测试控制器。

Example for mocking username and SID on MVC4. 在MVC4上模拟用户名和SID的示例。 The username and SID (Windows Authentication) in the following action should be tested: 应测试以下操作中的用户名和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();
    }
}

I use Moq and Visual Studio Test Tools . 我使用MoqVisual Studio测试工具 The test is implemented as follows: 测试实施如下:

[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");
}

I've changed dev environment global.asax and Web.Config for use FormsAuth for force a specific user. 我已经更改了dev环境global.asax和Web.Config,以便使用FormsAuth来强制特定用户。 The username uses the same WindowsAuth format. 用户名使用相同的WindowsAuth格式。 See: 看到:

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

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

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

The Windows or Forms Auth shares the same login patterns . Windows或Forms Auth共享相同的登录模式

The application will work with both Windows authentication and Form authentication. 该应用程序将与Windows身份验证和表单身份验证一起使用。

To mock WindowsIdentity you can do the following: 要模拟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);

then use mockedPrincipal.Object to get the actual WindowsIdentity 然后使用mockedPrincipal.Object来获取实际的WindowsIdentity

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM