ASP.NET Core 8.0 MVC 和身份:模拟时出错,System.NotSupportedException:默认 UI 需要具有电子邮件支持的用户存储

问题描述 投票:0回答:1

我正在使用 Moq 创建单元测试,以实现 Microsoft Scaffold Identity 自动生成的类的 100% 覆盖率,特别是

Register.cs.cshtml
。但是,我在一些测试中遇到了这个错误:

System.NotSupportedException:默认 UI 需要具有电子邮件支持的用户存储

以下是片段

  1. 日志错误,
  2. 我如何设置模拟测试,
  3. 我如何配置 Microsoft 身份属性 (
    Program.cs
    ),
  4. RegisterModel
    的参数,这是我需要测试的类和
  5. 发生错误的
    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
,但它似乎不起作用。

尽管多次尝试解决此问题,但我仍然遇到问题。我非常感谢有关正确配置模拟以包括测试电子邮件支持的任何帮助或见解。谢谢!

asp.net-core asp.net-core-mvc asp.net-core-identity
1个回答
0
投票

回答我自己的问题,以防其他人将来遇到同样的问题:

在提供的代码中,即使我们在模拟时将

IUserStore
扩展为
IEmailUserStore
,我们仍然需要在将参数传递给
DefaultUI
之前手动设置布尔值,以启用
_userManagerMock
中的
_signInManager
的电子邮件支持:

_ = _userManagerMock.Setup(x => x.SupportsUserEmail)
    .Returns(true);
© www.soinside.com 2019 - 2024. All rights reserved.