我正在使用 Moq 创建单元测试,以实现 Microsoft Scaffold Identity 自动生成的类的 100% 覆盖率,特别是
Register.cs.cshtml
。但是,我在一些测试中遇到了这个错误:
System.NotSupportedException:默认 UI 需要具有电子邮件支持的用户存储
以下是片段
Program.cs
),RegisterModel
的参数,这是我需要测试的类和GetEmailStore()
方法。日志错误:
错误信息:
System.NotSupportedException:默认 UI 需要具有电子邮件支持的用户存储。
堆栈跟踪:
在 /home/ygorgsena/eam/Main/Areas/Identity/Pages/Account/Register.cshtml.cs 中的 EAM.Areas.Identity.Pages.Account.RegisterModel.GetEmailStore():第 219 行
在 /home/ygorgsena/eam/Main/Areas 中的 EAM.Areas.Identity.Pages.Account.RegisterModel..ctor(SignInManager`1 signInManager、UserManager`1 userManager、IUserStore`1 userStore、ILogger`1 记录器、IEmailSender emailSender) /Identity/Pages/Account/Register.cshtml.cs:第 44 行
在 /home/ygorgsena/eam/Tests/Unit/RegisterUnitTest.cs 中的 EAM.Tests.Unit.RegisterUnitTest..ctor() 处:第 91 行
在System.RuntimeType.CreateInstanceDefaultCtor(布尔publicOnly,布尔wrapExceptions)
我如何设置模拟:
public class RegisterModelTests
{
private readonly Mock<UserManager<IdentityUser>> _userManagerMock;
private readonly Mock<SignInManager<IdentityUser>> _signInManagerMock;
private readonly Mock<IUserStore<IdentityUser>> _userStoreMock;
private readonly Mock<IUserEmailStore<IdentityUser>> _emailStoreMock;
private readonly Mock<ILogger<RegisterModel>> _loggerMock;
private readonly Mock<IEmailSender> _emailSenderMock;
private readonly RegisterModel _pageModel;
public RegisterModelTests()
{
_userStoreMock = new Mock<IUserStore<IdentityUser>>();
_userManagerMock = new Mock<UserManager<IdentityUser>>(
_userStoreMock.Object,
new Mock<IOptions<IdentityOptions>>().Object,
new Mock<IPasswordHasher<IdentityUser>>().Object,
Array.Empty<IUserValidator<IdentityUser>>(),
Array.Empty<IPasswordValidator<IdentityUser>>(),
new Mock<ILookupNormalizer>().Object,
new Mock<IdentityErrorDescriber>().Object,
new Mock<IServiceProvider>().Object,
new Mock<ILogger<UserManager<IdentityUser>>>().Object);
_signInManagerMock = new Mock<SignInManager<IdentityUser>>(
_userManagerMock.Object,
new Mock<IHttpContextAccessor>().Object,
new Mock<IUserClaimsPrincipalFactory<IdentityUser>>().Object,
new Mock<IOptions<IdentityOptions>>().Object,
new Mock<ILogger<SignInManager<IdentityUser>>>().Object,
new Mock<IAuthenticationSchemeProvider>().Object,
new Mock<IUserConfirmation<IdentityUser>>().Object);
_emailStoreMock = _userStoreMock.As<IUserEmailStore<IdentityUser>>();
_loggerMock = new Mock<ILogger<RegisterModel>>();
_emailSenderMock = new Mock<IEmailSender>();
_pageModel = new RegisterModel(
_userManagerMock.Object, _userStoreMock.Object, _signInManagerMock.Object, _loggerMock.Object, _emailSenderMock.Object);
}
[Fact]
public void GetEmailStore_ThrowsNotSupportedException_WhenUserManagerDoesNotSupportEmail()
{
// Arrange
_ = _userManagerMock.Setup(m => m.SupportsUserEmail).Returns(false);
// Use reflection to access the private GetEmailStore method
MethodInfo? methodInfo = typeof(RegisterModel).GetMethod("GetEmailStore", BindingFlags.NonPublic | BindingFlags.Instance);
// Act & Assert
Assert.NotNull(methodInfo);
_ = Assert.Throws<NotSupportedException>(() => methodInfo.Invoke(_pageModel, null));
}
我如何在
Program.cs
中配置 Microsoft 身份属性:
builder.Services.AddDefaultIdentity<IdentityUser>(options => options.SignIn.RequireConfirmedAccount = true).AddEntityFrameworkStores<EAMContext>();
RegisterModel
类的参数:
public class RegisterModel : PageModel
{
private readonly SignInManager<IdentityUser> _signInManager;
private readonly UserManager<IdentityUser> _userManager;
private readonly IUserStore<IdentityUser> _userStore;
private readonly IUserEmailStore<IdentityUser> _emailStore;
private readonly ILogger<RegisterModel> _logger;
private readonly IEmailSender _emailSender;
public RegisterModel(
UserManager<IdentityUser> userManager,
IUserStore<IdentityUser> userStore,
SignInManager<IdentityUser> signInManager,
ILogger<RegisterModel> logger,
IEmailSender emailSender)
{
_userManager = userManager;
_userStore = userStore;
_emailStore = GetEmailStore();
_signInManager = signInManager;
_logger = logger;
_emailSender = emailSender;
}
我尝试查看源代码以了解
IUserEmailStore<TUser>
的工作原理。该代码位于此link。这个 GetEmailStore()
方法就是发生错误的地方。
private IUserEmailStore<TUser> GetEmailStore()
{
if (Store is not IUserEmailStore<TUser> emailStore)
{
throw new NotSupportedException(Resources.StoreNotIUserEmailStore);
}
return emailStore;
}
第 139 行定义的
Store
字段的类型为
protected internal IUserStore<TUser> Store { get; set; }
GetEmailStore()
方法访问IUserStore
类型的属性并检查IUserEmailStore
是否存在。
正如您在“如何设置模拟”部分中所看到的,我尝试先模拟
IUserStore
,然后再模拟 IUserEmailStore
,但它似乎不起作用。
尽管多次尝试解决此问题,但我仍然遇到问题。我非常感谢有关正确配置模拟以包括测试电子邮件支持的任何帮助或见解。谢谢!
回答我自己的问题,以防其他人将来遇到同样的问题:
在提供的代码中,即使我们在模拟时将
IUserStore
扩展为IEmailUserStore
,我们仍然需要在将参数传递给DefaultUI
之前手动设置布尔值,以启用_userManagerMock
中的_signInManager
的电子邮件支持:
_ = _userManagerMock.Setup(x => x.SupportsUserEmail)
.Returns(true);