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?