Тестирование .Net Core Identity GetUserAsync - PullRequest
0 голосов
/ 11 апреля 2019

Используя .NET Core 2.2, я пытаюсь протестировать страницу Razor с помощью Identity Framework, который начинается следующим образом

var user = await _userManager.GetUserAsync(User);

Существует множество онлайн-решений, которые могут быть обобщены этим общим классом: настройка UserManager на макет IUserStore и настройка User на PageContext.

public class SetupContext<T> where T : IdentityUser<Guid>, new()
{
    public Mock<IUserRoleStore<T>> MockUserStore { get; private set; }

    public UserManager<T> SetupUserManager()
    {
        MockUserStore = new Mock<IUserStore<T>>().As<IUserRoleStore<T>>();
        MockUserStore.Setup(x => x.FindByIdAsync(It.IsAny<string>(), It.IsAny<CancellationToken>())).ReturnsAsync(
            (string id, CancellationToken token) => new T()
            {
                Id = Guid.Parse(id),
                UserName = "User name"
            });
        return new UserManager<T>(MockUserStore.Object, null, null, null, null, null, null, null, null);
    }

    public PageContext SetupPageContext()
    {
        var displayName = "User name";
        var identity = new GenericIdentity(displayName);
        var principle = new ClaimsPrincipal(identity);
        var httpContext = new DefaultHttpContext()
        {
            User = principle
        };
        var modelState = new ModelStateDictionary();
        var actionContext = new ActionContext(httpContext, new RouteData(), new PageActionDescriptor(), modelState);
        var modelMetadataProvider = new EmptyModelMetadataProvider();
        var viewData = new ViewDataDictionary(modelMetadataProvider, modelState);
        var pageContext = new PageContext(actionContext)
        {
            ViewData = viewData
        };
        return pageContext;
    }
}

Проблема в том, что это не сработает. GetUserAsync вызывает GetUserId, который вызывает

principal.FindFirstValue(Options.ClaimsIdentity.UserIdClaimType)

Это утверждение не настроено, фактически нигде не установлено ни одного идентификатора, поэтому оно не может вернуть ни пользователя, ни идентификатор пользователя. Затем позже я захочу получить доступ к данным базы данных, связанным с UserId, для тестирования.

Я потратил довольно много времени на поиск и не нашел решения. Похоже, я там на 90%, но что еще мне нужно, чтобы правильно настроить тест?

Тест, который я буду запускать, будет настроен так

private async Task<FreeTrialModel> SetupModelAsync()
{
    _db = new RemoteHealingTechDbContext(InMemoryDbContext<RemoteHealingTechDbContext>.GetTestDbOptions());
    await SeedData.Initialize(_db);
    var _setup = new SetupContext<ApplicationUser>();
    var userManager = _setup.SetupUserManager();
    var pageContext = _setup.SetupPageContext();
    return new FreeTrialModel(_db, userManager)
    {
        PageContext = pageContext
    };
}

1 Ответ

0 голосов
/ 12 апреля 2019

После еще копания я наконец нашел ответ здесь

Я должен использовать ClaimsIdentity, содержащий Claims, вместо GenericIdentity. Заменить

var identity = new GenericIdentity(displayName);

с

var identity = new ClaimsIdentity(new Claim[]
{
    new Claim(ClaimTypes.NameIdentifier, userName, null)
});

Затем метод FindByIdAsync принимает имя пользователя, а не идентификатор пользователя, поэтому я изменил объявление на

MockUserStore.Setup(x => x.FindByIdAsync(It.IsAny<string>(), It.IsAny<CancellationToken>())).ReturnsAsync(
    (string userName, CancellationToken token) => new T()
    {
        Id = userId,
        UserName = userName
    });

Общее решение

/// <summary>
/// Configures the .Net Core Identity Framework for unit testing.
/// </summary>
/// <typeparam name="T">The type of IdentityUser used for the framework.</typeparam>
/// <typeparam name="TKey">The data type of primary keys.</typeparam>
public class SetupContext<T, TKey> where T : IdentityUser<TKey>, new() where TKey : IEquatable<TKey>
{
    /// <summary>
    /// Returns the generated IUserRoleStore mock.
    /// </summary>
    public Mock<IUserRoleStore<T>> MockUserStore { get; private set; }

    /// <summary>
    /// Returns a UserManager based on a IUserRoleStore mock.
    /// </summary>
    /// <param name="userId">The UserId to set on generated IdentityUser objects.</param>
    /// <returns>A new UserManager for unit testing.</returns>
    public UserManager<T> SetupUserManager(TKey userId = default(TKey))
    {
        MockUserStore = new Mock<IUserStore<T>>().As<IUserRoleStore<T>>();
        MockUserStore.Setup(x => x.FindByIdAsync(It.IsAny<string>(), It.IsAny<CancellationToken>())).ReturnsAsync(
            (string userName, CancellationToken token) => new T()
            {
                Id = userId,
                UserName = userName
            });
        return new UserManager<T>(MockUserStore.Object, null, null, null, null, null, null, null, null);
    }

    /// <summary>
    /// Returns a PageContext to set on a page, containing a logged in user.
    /// </summary>
    /// <param name="userName">The user name to set on the ClaimsIdentity.</param>
    /// <returns>A new PageContext object.</returns>
    public PageContext SetupPageContext(string userName = "User name")
    {
        var identity = new ClaimsIdentity(new Claim[]
        {
            new Claim(ClaimTypes.NameIdentifier, userName, null)
        });
        var principle = new ClaimsPrincipal(identity);
        var httpContext = new DefaultHttpContext()
        {
            User = principle
        };
        var modelState = new ModelStateDictionary();
        var actionContext = new ActionContext(httpContext, new RouteData(), new PageActionDescriptor(), modelState);
        var modelMetadataProvider = new EmptyModelMetadataProvider();
        var viewData = new ViewDataDictionary(modelMetadataProvider, modelState);
        var pageContext = new PageContext(actionContext)
        {
            ViewData = viewData
        };
        return pageContext;
    }
}
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...