11

I am struggling to implement unit testing for action methods that incorporate User.Identity.Name functionality. The methods that I've come across fail because the properties that they suggest writing to throw 'read-only' errors (e.g. writing to HttpContext or the controller User)

I have an action method:

[Authorize] public async Task<ViewResult> EditProject(int projectId) { Project project = repository.Projects.FirstOrDefault(p => p.ProjectID == projectId); if (project != null) { //HOW DO I MOCK USER.IDENTITY.NAME FOR THIS PORTION? var user = await userManager.FindByNameAsync(User.Identity.Name); bool owned = await checkIfUserOwnsItem(project.UserID, user); if (owned) { return View(project); } else { TempData["message"] = $"User is not authorized to view this item"; } } return View("Index"); } 

If I want to unit test this action method, how can I mock up the User.Identity object?

[Fact] public async Task Can_Edit_Project() { //Arrange var user = new AppUser() { UserName = "JohnDoe", Id = "1" }; Mock<IRepository> mockRepo = new Mock<IRepository>(); mockRepo.Setup(m => m.Projects).Returns(new Project[] { new Project {ProjectID = 1, Name = "P1", UserID = "1"}, new Project {ProjectID = 2, Name = "P2", UserID = "1"}, new Project {ProjectID = 3, Name = "P3", UserID = "1"}, }); Mock<ITempDataDictionary> tempData = new Mock<ITempDataDictionary>(); Mock<UserManager<AppUser>> userMgr = GetMockUserManager(); //Arrange ProjectController controller = new ProjectController(mockRepo.Object, userMgr.Object) { TempData = tempData.Object, }; //HOW WOULD I MOCK THE USER.IDENTITY.NAME HERE? //The example below causes two errors: // 1) 'Invalid setup on a non-virtual (overridable in VB) member //mock => mock.HttpContext //and 2) HttpContext does not contain a definition for IsAuthenticated var mock = new Mock<ControllerContext>(); mock.SetupGet(p => p.HttpContext.User.Identity.Name).Returns(user.UserName); mock.SetupGet(p => p.HttpContext.Request.IsAuthenticated).Returns(true); //Act var viewResult = await controller.EditProject(2); Project result = viewResult.ViewData.Model as Project; //Assert Assert.Equal(2, result.ProjectID); } 

EDIT: Making some progress by adding the code below.

var claims = new List<Claim>() { new Claim(ClaimTypes.Name, "John Doe"), new Claim(ClaimTypes.NameIdentifier, "1"), new Claim("name", "John Doe"), }; var identity = new ClaimsIdentity(claims, "TestAuthType"); 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); 

The User.Identity.Name is set properly now, but the line below still returns a user = null

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

How can I make sure my mocked UserManager can return a mocked logged in user?

1
  • Just set it through ControllerContext Commented Mar 16, 2018 at 22:17

3 Answers 3

21

Set your fake User through ControllerContext

var context = new ControllerContext { HttpContext = new DefaultHttpContext { User = fakeUser } }; // Then set it to controller before executing test controller.ControllerContext = context; 
Sign up to request clarification or add additional context in comments.

4 Comments

Do I have to use implicit conversion from AppUser to ClaimsPrincipal to get this to work? This gives me a 'method or operation not implemented' error
Where is fakeUser being set?
@AndrewGray fakeUser would be something like new User(any special settings you want)
To set fakeUser, see answer by Fancy5g and my comment there.
12

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

2 Comments

What is the : GetMockUserManager() ? I just can't access it, it doesn't exists
@ChristopheChenel It's a custom function to return a mocked UserManager. I'll add it to the answer.
3

You can create fakeContext and use that. See below:

var fakeContext = new Mock<HttpContextBase>(); var fakeIdentity = new GenericIdentity("User"); var principal = new GenericPrincipal(fakeIdentity, null); fakeContext.Setup(x => x.User).Returns(principal); var projectControllerContext = new Mock<ControllerContext>(); projectControllerContext.Setup(x => x.HttpContext).Returns(fakeContext.Object); 

3 Comments

Should this be working in Core? I get an error on HttpContextBase (namespace not found) and an error on principal 'cannot convert from GenericPrincipal to ?'
this wont work in asp net core.
You can piece together with other answers and make it work. At least the var fakeIdentity = new GenericIdentity("tester"); part is helpful. Now you can add var fakeUser = new ClaimsPrincipal(fakeIdentity); and make the answer by Fabio work.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.