Mocking User.Identity in ASP.NET MVC

后端 未结 6 887
眼角桃花
眼角桃花 2020-12-13 06:23

I need to create Unit Tests for an ASP.NET MVC 2.0 web site. The site uses Windows Authentication.

I\'ve been reading up on the necessity to mock the HTTP context

相关标签:
6条回答
  • 2020-12-13 07:00

    To mock WindowsIdentity you can do the following:

    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

    0 讨论(0)
  • 2020-12-13 07:16

    I don't know for MVC 2.0, but in newer versions you can mock the 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#?

    0 讨论(0)
  • 2020-12-13 07:16

    Scott Hanselman shows in his blog how to use IPrincipal and ModelBinder to make easier to test the controller by mocking IPrincipal.

    0 讨论(0)
  • 2020-12-13 07:18

    I've used IoC to abstract this away with some success. 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. For unit tests, you could add another constructor to allow you bypass the IIdentity dependency.

    And then I use Ninject (pick your favorite IoC container, doesn't matter), and created a binding for IIdentity as such:

    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. 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. You might be able to adapt this example to Windows Authentication.

    0 讨论(0)
  • 2020-12-13 07:20

    I've changed dev environment global.asax and Web.Config for use FormsAuth for force a specific user. The username uses the same WindowsAuth format. 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.

    The application will work with both Windows authentication and Form authentication.

    0 讨论(0)
  • 2020-12-13 07:25

    Example for mocking username and SID on MVC4. The username and SID (Windows Authentication) in the following action should be tested:

    [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. 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");
    }
    
    0 讨论(0)
提交回复
热议问题