使用NUnit与Moq做单元测试时,UserManager与Identity模拟遇异常
我来帮你搞定这两个单元测试里的坑,都是模拟ASP.NET Identity组件时的常见问题,咱们一个个解决:
问题1:UserManager.CreateIdentityAsync返回null导致引用错误
你现在只Setup了UserManager.Users属性,但没有对CreateIdentityAsync方法进行模拟设置。Moq对于未Setup的虚方法/接口方法,默认会返回对应类型的默认值(引用类型就是null),所以执行CreateIdentityAsync时自然得到null,后续调用identity.AddClaim就会抛出空引用异常。
修复方案:显式Setup CreateIdentityAsync方法
你需要手动模拟这个异步方法的返回值,返回一个有效的ClaimsIdentity实例:
var users = new List<MstUser> { new MstUser { Id=new Guid("b35ec046-423e-4ee4-a057-fcb1fe182086"), Email="niteshkmodi@gmail.com", UserName = "niteshkmodi@gmail.com" } }.AsQueryable(); var userManager = new Mock<ApplicationUserManager>(); userManager.Setup(x => x.Users).Returns(users); // 新增:模拟CreateIdentityAsync,返回一个有效的ClaimsIdentity var testIdentity = new ClaimsIdentity(DefaultAuthenticationTypes.ApplicationCookie); userManager.Setup(x => x.CreateIdentityAsync(It.IsAny<MstUser>(), It.IsAny<string>())) .ReturnsAsync(testIdentity); var controller = new HomeController(userManager.Object, adminBl); ViewResult result = await controller.Index(model, RedirectToAction) as ViewResult;
如果需要在返回的Identity里预先添加Claim(比如你的代码里要加FullName),也可以在创建testIdentity的时候直接添加:
var testIdentity = new ClaimsIdentity(DefaultAuthenticationTypes.ApplicationCookie); testIdentity.AddClaim(new Claim(IdentityClaimConst.FullName, adminUser.Name ?? ""));
问题2:User.Identity.GetUserId()出现类型转换错误
GetUserId()是ClaimsIdentity的扩展方法,它内部会尝试将User.Identity强制转换为ClaimsIdentity类型。但你现在模拟的IPrincipal返回的Identity是Moq自动生成的Castle.Proxies.ObjectProxy(默认的Mock对象),不是ClaimsIdentity实例,所以会抛出类型转换异常。
修复方案:模拟ClaimsIdentity并关联到Principal
你需要创建一个真实的ClaimsIdentity实例(或者正确模拟的ClaimsIdentity),并让IPrincipal的Identity属性返回这个实例,同时要添加ClaimTypes.NameIdentifier类型的Claim(这是GetUserId()读取的Claim):
var userId = "b35ec046-423e-4ee4-a057-fcb1fe182086"; // 测试用的用户ID // 创建带UserId Claim的ClaimsIdentity var identity = new ClaimsIdentity(new[] { new Claim(ClaimTypes.NameIdentifier, userId) }, DefaultAuthenticationTypes.ApplicationCookie); // 模拟IPrincipal,让它的Identity返回这个ClaimsIdentity var principal = new Mock<IPrincipal>(); principal.Setup(x => x.Identity).Returns(identity); var controllerContext = new Mock<ControllerContext>(); controllerContext.Setup(x => x.HttpContext.User).Returns(principal.Object); var controller = new HomeController(adminBl); controller.ControllerContext = controllerContext.Object; JsonResult result = controller.MyProfile(null, null) as JsonResult;
这样调用User.Identity.GetUserId()时,就能正确转换为ClaimsIdentity并读取到预设的用户ID了。
内容的提问来源于stack exchange,提问作者niteshkumarmodi

