How to mock Controller.User using moq

28,935

Solution 1

You need to Mock the ControllerContext, HttpContextBase and finally IPrincipal to mock the user property on Controller. Using Moq (v2) something along the following lines should work.

    [TestMethod]
    public void HomeControllerReturnsIndexViewWhenUserIsAdmin() {
        var homeController = new HomeController();

        var userMock = new Mock<IPrincipal>();
        userMock.Expect(p => p.IsInRole("admin")).Returns(true);

        var contextMock = new Mock<HttpContextBase>();
        contextMock.ExpectGet(ctx => ctx.User)
                   .Returns(userMock.Object);

        var controllerContextMock = new Mock<ControllerContext>();
        controllerContextMock.ExpectGet(con => con.HttpContext)
                             .Returns(contextMock.Object);

        homeController.ControllerContext = controllerContextMock.Object;
        var result = homeController.Index();
        userMock.Verify(p => p.IsInRole("admin"));
        Assert.AreEqual(((ViewResult)result).ViewName, "Index");
    }

Testing the behaviour when the user isn't an admin is as simple as changing the expectation set on the userMock object to return false.

Solution 2

Using Moq version 3.1 (and NUnit):

    [Test]
    public void HomeController_Index_Should_Return_Non_Null_ViewPage()
    {
        // Assign:
        var homeController = new HomeController();

        Mock<ControllerContext> controllerContextMock = new Mock<ControllerContext>();
        controllerContextMock.Setup(
            x => x.HttpContext.User.IsInRole(It.Is<string>(s => s.Equals("admin")))
            ).Returns(true);
        homeController.ControllerContext = controllerContextMock.Object;

        // Act:
        ActionResult index = homeController.Index();

        // Assert:
        Assert.IsNotNull(index);
        // Place other asserts here...
        controllerContextMock.Verify(
            x => x.HttpContext.User.IsInRole(It.Is<string>(s => s.Equals("admin"))),
            Times.Exactly(1),
            "Must check if user is in role 'admin'");
    }

Notice that there is no need to create mock for HttpContext, Moq supports nesting of properties when setting up the test.

Solution 3

When using AspNetCore I could not mock the ControllerContext since I got an exception.

Unsupported expression: m => m.HttpContext
Non-overridable members (here: ActionContext.get_HttpContext) may not be used in setup / verification expressions.

Instead I had to mock the HttpContext and create a ControllerContext and pass the HttpContext object along.

I did find that mocking claims or response/request objects works as well when using this method.

[Test]
public void TestSomeStuff() {
  var name = "some name";

  var httpContext = new Mock<HttpContext>();
  httpContext.Setup(m => m.User.IsInRole("RoleName")).Returns(true);
  httpContext.Setup(m => m.User.FindFirst(ClaimTypes.Name)).Returns(name);

  var context = new ControllerContext(new ActionContext(httpContext.Object, new RouteData(), new ControllerActionDescriptor()));

  var controller = new MyController()
  {
    ControllerContext = context
  };

  var result = controller.Index();
  Assert.That(result, Is.Not.Null);
}
Share:
28,935
Eugenio Miró
Author by

Eugenio Miró

Developer VB, C#, C++, ASP.NET, ASP.NET MVC, javascript, some PHP, some SQL Server, some MySQL

Updated on July 09, 2022

Comments

  • Eugenio Miró
    Eugenio Miró almost 2 years

    I have a couple of ActionMethods that queries the Controller.User for its role like this

    bool isAdmin = User.IsInRole("admin");
    

    acting conveniently on that condition.

    I'm starting to make tests for these methods with code like this

    [TestMethod]
    public void HomeController_Index_Should_Return_Non_Null_ViewPage()
    {
        HomeController controller  = new HomePostController();
        ActionResult index = controller.Index();
    
        Assert.IsNotNull(index);
    }
    

    and that Test Fails because Controller.User is not set. Any idea?