测试.Net Core身份GetUserAsync

时间:2019-04-11 11:40:55

标签: unit-testing asp.net asp.net-mvc .net-core

使用.NET Core 2.2,我正在尝试使用以这种方式启动的Identity Framework测试Razor页面

var user = await _userManager.GetUserAsync(User);

此通用类可以总结出许多在线解决方案:在IUserStore模拟中设置UserManager,在PageContext上设置User。

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)

此声明未设置,实际上任何位置均未设置ID,因此无法返回用户或用户ID。然后,我将要访问与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 :(得分:0)

经过进一步的挖掘,我终于找到了答案here

我必须使用包含Claims而不是GenericIdentity的ClaimsIdentity。替换

var identity = new GenericIdentity(displayName);

使用

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

然后,FindByIdAsync方法使用UserName而不是UserId,因此我将声明更改为

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;
    }
}