Mocking Iprincipal in ASP.NET Core

Mocking IPrincipal in ASP.NET Core

The controller’s User is accessed through the HttpContext of the controller. The latter is stored within the ControllerContext.

The easiest way to set the user is by assigning a different HttpContext with a constructed user. We can use DefaultHttpContext for this purpose, that way we don’t have to mock everything. Then we just use that HttpContext within a controller context and pass that to the controller instance:

var user = new ClaimsPrincipal(new ClaimsIdentity(new Claim[]
{
new Claim(ClaimTypes.Name, "example name"),
new Claim(ClaimTypes.NameIdentifier, "1"),
new Claim("custom-claim", "example claim value"),
}, "mock"));

var controller = new SomeController(dependencies…);
controller.ControllerContext = new ControllerContext()
{
HttpContext = new DefaultHttpContext() { User = user }
};

When creating your own ClaimsIdentity, make sure to pass an explicit authenticationType to the constructor. This makes sure that IsAuthenticated will work correctly (in case you use that in your code to determine whether a user is authenticated).

Mock User.Identity in ASP.NET Core for Unit Testing

To unit test my action method that uses

var user = await userManager.FindByNameAsync(User.Identity.Name); 

I needed to:

  1. set up my user

var user = new AppUser() { UserName = "JohnDoe", Id = "1" };


  1. Set up my HttpContext to give data to return user.UserName in the User.Identity.Name object in the controller
var claims = new List<Claim>()
{
new Claim(ClaimTypes.Name, user.UserName),
new Claim(ClaimTypes.NameIdentifier, user.Id),
new Claim("name", user.UserName),
};
var identity = new ClaimsIdentity(claims, "Test");
var claimsPrincipal = new ClaimsPrincipal(identity);

var mockPrincipal = new Mock<IPrincipal>();
mockPrincipal.Setup(x => x.Identity).Returns(identity);
mockPrincipal.Setup(x => x.IsInRole(It.IsAny<string>())).Returns(true);

var mockHttpContext = new Mock<HttpContext>();
mockHttpContext.Setup(m => m.User).Returns(claimsPrincipal);

  1. Setup the mock UserManager to return the user object on the FindByNameAsync method
Mock<UserManager<AppUser>> userMgr = GetMockUserManager();
userMgr.Setup(x => x.FindByNameAsync(It.IsAny<string>())).ReturnsAsync(user);

Edit:

public Mock<UserManager<AppUser>> GetMockUserManager()
{
var userStoreMock = new Mock<IUserStore<AppUser>>();
return new Mock<UserManager<AppUser>>(
userStoreMock.Object, null, null, null, null, null, null, null, null);
}

How to mock HttpContext.User

Make a mock http context

private class MockHttpContext : HttpContextBase {
private readonly IPrincipal user;

public MockHttpContext(IPrincipal principal) {
this.user = principal;
}

public override IPrincipal User {
get {
return user;
}
set {
base.User = value;
}
}
}

Arrange test accordingly.

[Test]
public void ViewDocuments_WhenCalled_ShouldReturnViewModel() {
// Arrange
var principal = new CustomPrincipal("2038786");
principal.UserId = "2038786";
principal.FirstName = "Test";
principal.LastName = "User";
principal.IsStoreUser = true;

var mockUoW = new Mock<IUnitOfWork>();
//...setup UoW dependency if needed
var controller = new DocumentsController(mockUoW.Object);
controller.ControllerContext = new ControllerContext {
Controller = controller,
HttpContext = new MockHttpContext(principal)
};

// Act
var result = controller.ViewDocuments();

//Assert
//...assertions
}

Don't mock system under test. Mock its dependencies.

Update: Passing HttpContext and IPrincipal to Controler in ReSharper unit test

Following a similar solution mentioned in this post I added the following to the end of the SetUp() method.

        var controllerCtx = new ControllerContext();
controllerCtx.HttpContext = new HttpContextWrapper(HttpContext.Current);
_controlAgtTran.ControllerContext = controllerCtx;

Wrapping the current HttpContext inside an HttpContextBase property (the inappropriately named controllerCtx.HttpContext) the test now has access to the User and HttpContext properties of the controller. These properties were previously read-only when using just the HttpContext.Current session and therefore always null.

FYI - this is my first time unit testing with these objects so that explanation may be less than 100% correct. Please feel free to comment below and I'll make any necessary changes.

How mock User.Identity

You need to mock the HttpContext on the controller for example :

var httpContext = new DefaultHttpContext()
{
User = new System.Security.Claims.ClaimsPrincipal(new GenericIdentity("username"))
};
var actionContext = new ActionContext(httpContext, new Microsoft.AspNetCore.Routing.RouteData(),
new Microsoft.AspNetCore.Mvc.Controllers.ControllerActionDescriptor());
cardController.ControllerContext = new Microsoft.AspNetCore.Mvc.ControllerContext(actionContext);

ClaimsPrincipal.HttpContext in .Net 5

Finally managed to solve it, leaving the answer here for anyone who might face a similar situation.

Instead of replacing the ControllerContext completely, I replaced ControllerContext.HttpContext

var user = new ClaimsPrincipal(new ClaimsIdentity(new Claim[]
{
new Claim(ClaimTypes.Name, userName),

}, "mock"));
userController.ControllerContext.HttpContext = new DefaultHttpContext() { User = user };

This ensured the Controller could receive UserName with

var userName = User.Identity.Name;


Related Topics



Leave a reply



Submit