使用Moq和xUnit进行单元测试的新手。我试图模拟控制器构造函数中使用的SignInManager
来构建单元测试。我可以为SignInManager
构造函数找到的文档说它接受UserManager
和AuthenticationManager
对象:https://msdn.microsoft.com/en-us/library/mt173769(v=vs.108).aspx#M:Microsoft.AspNet.Identity.Owin.SignInManager`2。
当我尝试模拟控制器时,我收到错误消息,说它无法实例化SignInManager
和AuthenticationManager
类的代理。
错误:
"消息: Castle.DynamicProxy.InvalidProxyConstructorArgumentsException:可以 没有实例化类的代理: Microsoft.AspNetCore.Identity.SignInManager1 [Models.AppUser, ,版本= 1.0.0.0,文化=中立, 公钥=空]。找不到匹配的构造函数 给定参数:Castle.Proxies.UserManager`1Proxy Castle.Proxies.AuthenticationManagerProxy"
单元测试:
public void Can_Send_Password_Reset_Email()
{
//Arrange
//create mock services
Mock<IEmailService> mockEmailService = new Mock<IEmailService>();
Mock<ILessonRepository> mockRepo = new Mock<ILessonRepository>();
Mock<UserManager<AppUser>> mockUsrMgr = GetMockUserManager();
var mockSignInMgr = GetMockSignInManager();
Mock<UserValidator<AppUser>> mockUsrVal = new Mock<UserValidator<AppUser>>();
Mock<PasswordValidator<AppUser>> mockPwdVal = new Mock<PasswordValidator<AppUser>>();
Mock<PasswordHasher<AppUser>> mockPwdHshr = new Mock<PasswordHasher<AppUser>>();
Mock<ForgotPasswordModel> model = new Mock<ForgotPasswordModel>();
model.Object.Email = "joe@example.com";
var user = new AppUser();
var token = mockUsrMgr.Object.GeneratePasswordResetTokenAsync(user).Result;
//create mock temporary data, needed for controller message
Mock<ITempDataDictionary> tempData = new Mock<ITempDataDictionary>();
//create the controller
//ERROR ON THIS LINE
AccountController controller = new AccountController(mockUsrMgr.Object, mockSignInMgr.Object, mockUsrVal.Object, mockPwdVal.Object, mockPwdHshr.Object, mockEmailService.Object)
{
TempData = tempData.Object
};
//Act
//the controller should call the email action method
controller.PasswordResetEmail(model.Object);
//Assert
//verify that the email service method was called one time
mockEmailService.Verify(m => m.PasswordResetMessage(user, "Test Email"), Times.Once());
}
SignInManager模拟功能:
//create a mock SignInManager class
private Mock<SignInManager<AppUser>> GetMockSignInManager()
{
var mockUsrMgr = GetMockUserManager();
var mockAuthMgr = new Mock<AuthenticationManager>();
return new Mock<SignInManager<AppUser>>(mockUsrMgr.Object, mockAuthMgr.Object);
}
GetMockUserManager()
在其他单元测试中运行良好,但似乎不是问题。
答案 0 :(得分:7)
这就是我的方法,希望对您有帮助。
public class FakeSignInManager : SignInManager<ApplicationUser>
{
public FakeSignInManager()
: base(new Mock<FakeUserManager>().Object,
new Mock<IHttpContextAccessor>().Object,
new Mock<IUserClaimsPrincipalFactory<ApplicationUser>>().Object,
new Mock<IOptions<IdentityOptions>>().Object,
new Mock<ILogger<SignInManager<ApplicationUser>>>().Object,
new Mock<IAuthenticationSchemeProvider>().Object)
{ }
}
public class FakeUserManager : UserManager<ApplicationUser>
{
public FakeUserManager()
: base(new Mock<IUserStore<ApplicationUser>>().Object,
new Mock<IOptions<IdentityOptions>>().Object,
new Mock<IPasswordHasher<ApplicationUser>>().Object,
new IUserValidator<ApplicationUser>[0],
new IPasswordValidator<ApplicationUser>[0],
new Mock<ILookupNormalizer>().Object,
new Mock<IdentityErrorDescriber>().Object,
new Mock<IServiceProvider>().Object,
new Mock<ILogger<UserManager<ApplicationUser>>>().Object)
{ }
public override Task<IdentityResult> CreateAsync(ApplicationUser user, string password)
{
return Task.FromResult(IdentityResult.Success);
}
public override Task<IdentityResult> AddToRoleAsync(ApplicationUser user, string role)
{
return Task.FromResult(IdentityResult.Success);
}
public override Task<string> GenerateEmailConfirmationTokenAsync(ApplicationUser user)
{
return Task.FromResult(Guid.NewGuid().ToString());
}
}
答案 1 :(得分:3)
您正在查看的文档适用于Microsoft.AspNet.Identity.Owin.SignInManager
。
如果查看错误消息,您会看到Moq实际上正在尝试创建Microsoft.AspNetCore.Identity.SignInManager
类型的对象。请注意AspNet
和AspNetCore
之间的区别。
基本上,您正在查看经典ASP.NET中旧的基于Owin的Identity堆栈中使用的SignInManager
。但是您需要查看ASP.NET Core版本。如果您查看该类型的文档,您会看到constructor has a few more dependencies:
public SignInManager(
UserManager<TUser> userManager,
IHttpContextAccessor contextAccessor,
IUserClaimsPrincipalFactory<TUser> claimsFactory,
IOptions<Microsoft.AspNetCore.Identity.IdentityOptions> optionsAccessor,
ILogger<Microsoft.AspNetCore.Identity.SignInManager<TUser>> logger,
IAuthenticationSchemeProvider schemes);
答案 2 :(得分:1)
对于ASP.NET Core 5,可以使用以下设置:
var userManagerMock = new Mock<UserManager<User>>(
/* IUserStore<TUser> store */Mock.Of<IUserStore<User>>(),
/* IOptions<IdentityOptions> optionsAccessor */null,
/* IPasswordHasher<TUser> passwordHasher */null,
/* IEnumerable<IUserValidator<TUser>> userValidators */null,
/* IEnumerable<IPasswordValidator<TUser>> passwordValidators */null,
/* ILookupNormalizer keyNormalizer */null,
/* IdentityErrorDescriber errors */null,
/* IServiceProvider services */null,
/* ILogger<UserManager<TUser>> logger */null);
var signInManagerMock = new Mock<SignInManager<User>>(
userManagerMock.Object,
/* IHttpContextAccessor contextAccessor */Mock.Of<IHttpContextAccessor>(),
/* IUserClaimsPrincipalFactory<TUser> claimsFactory */Mock.Of<IUserClaimsPrincipalFactory<User>>(),
/* IOptions<IdentityOptions> optionsAccessor */null,
/* ILogger<SignInManager<TUser>> logger */null,
/* IAuthenticationSchemeProvider schemes */null,
/* IUserConfirmation<TUser> confirmation */null);
答案 3 :(得分:0)
SignInManager
没有构造函数,没有参数,因此您必须在new Mock<SignInManager>(**Params here**)
中传递参数。
这些参数中的某些不能不为null,因此必须像这样嘲笑它们:
UserManager<TUser>
==>第一个参数。IHttpContextAccessor
==>第二个参数。这里的问题甚至是UserManager
也不缺少参数构造函数,因此您必须模拟其参数
对于Usermanager
,不应为空的参数IUserStore<User>
==>第一个参数
这就是我如何用最少的代码完成
var _mockUserManager = new Mock<ApiUserManager>(new Mock<IUserStore<ApiUser>>().Object,
null, null, null, null, null, null, null, null);
var _contextAccessor = new Mock<IHttpContextAccessor>();
var _userPrincipalFactory = new Mock<IUserClaimsPrincipalFactory<ApiUser>>();
Mock<ApiSignInManager>mockApiSignInManager = new Mock<ApiSignInManager>(_mockUserManager.Object,
_contextAccessor.Object, _userPrincipalFactory.Object, null, null, null);
的启发
答案 4 :(得分:0)
我在 ASP.NET Core 5 中通过 FakeIsEasy 直接伪造 let query = productsRef;
filters.map((filter) => {
query = query.where(filter.key, filter.operation, filter.value);
});
:
SignInManager<User>