_User.Identity.Name
_機能を組み込んだアクションメソッドの単体テストの実装に苦労しています。私が遭遇したメソッドは、「読み取り専用」エラーをスローするように書き込みを提案するプロパティ(たとえば、HttpContext
またはコントローラーUser
への書き込み)が原因で失敗します
アクションメソッドがあります。
_[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");
}
_
このアクションメソッドを単体テストする場合、_User.Identity
_オブジェクトをモックアップするにはどうすればよいですか?
_[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);
}
_
編集:以下のコードを追加することにより、いくつかの進歩を遂げています。
_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);
_
_User.Identity.Name
_が正しく設定されましたが、以下の行は_user = null
_を返します
var user = await userManager.FindByNameAsync(User.Identity.Name);
偽装したUserManager
が偽装したログインユーザーを確実に返すことができるようにするにはどうすればよいですか?
偽物を設定するUser
からControllerContext
var context = new ControllerContext
{
HttpContext = new DefaultHttpContext
{
User = fakeUser
}
};
// Then set it to controller before executing test
controller.ControllerContext = context;
FakeContextを作成して使用できます。下記参照:
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);
を使用する私のアクションメソッドを単体テストするには
var user = await userManager.FindByNameAsync(User.Identity.Name);
私はする必要がありました:
1)ユーザーを設定する
var user = new AppUser() { UserName = "JohnDoe", Id = "1" };
2)コントローラのuser.UserName
オブジェクトでUser.Identity.Name
を返すデータを提供するようにHttpContextを設定します
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);
3)UserManager
メソッドでuser
オブジェクトを返すようにモックFindByNameAsync
を設定します
Mock<UserManager<AppUser>> userMgr = GetMockUserManager();
userMgr.Setup(x => x.FindByNameAsync(It.IsAny<string>())).ReturnsAsync(user);