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:
- set up my user
var user = new AppUser() { UserName = "JohnDoe", Id = "1" };
- Set up my HttpContext to give data to return
user.UserName
in theUser.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);
- Setup the mock
UserManager
to return theuser
object on theFindByNameAsync
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
Automapper Convert from Multiple Sources
Linq to SQL Using Group by and Count(Distinct)
How to Draw Directly on the Windows Desktop, C#
How to Change Symbol for Decimal Point in Double.Tostring()
Select Parsed Int, If String Was Parseable to Int
Best Way to Access Com Objects from C#
Icecast 2: Protocol Description, Streaming to It Using C#
Routing: the Current Request for Action [...] Is Ambiguous Between the Following Action Methods
Built in .Net Algorithm to Round Value to the Nearest 10 Interval
Cannot Access a Disposed Object in ASP.NET Core When Injecting Dbcontext
Background Color of a Listbox Item (Windows Forms)
Why Does Enumerable.All Return True for an Empty Sequence
One to One Optional Relationship Using Entity Framework Fluent API