54

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?

3 Answers 3

76

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.

Sign up to request clarification or add additional context in comments.

2 Comments

In the latest versions of Moq, ExpectGet has been replaced by SetupGet.
Any way to do this if you are using the ClaimsPrincipal in the constructor of the controller?
27

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.

Comments

20

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

1 Comment

Thanks, awesome! One potential pitfall is that you want the RouteData from AspNetCore.Routing, not AspNetCore.Components. And to mock the name, I used httpContext.SetupGet(m => m.User.Identity.Name).Returns("name"); Lastly, you have a missing bracket on the line var context ....

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.