测试期间,不能将ClaimsIdentity用户识别为已登录

时间:2018-10-02 15:20:54

标签: c# unit-testing asp.net-identity claims-based-identity aspnetboilerplate

我正在尝试在我正在从事的新项目中建立单元测试。我将ASP.NET Boilerplate(.NET Core,多页面应用程序)与预先建立的身份验证作为起点。从Web.Tests is not included in this template开始,我试图从the ASP.NET Core template中提供的内容中创建自己的内容。我已经能够建立一个使用Startup进行测试的InMemoryDatabase类。但是,最基本的测试无法通过。我一直无法让测试用户完全通过身份验证并被识别为“已登录”。被测试的代码是这样的:

[AbpMvcAuthorize]
public class HomeController : ProjectControllerBase
{
    public ActionResult Index()
    {
        return View();
    }
}

测试是这样写的:

[Fact]
public async Task Index_Test()
{            
    // Added as part of suggestions made by 'https://medium.com/@zbartl/authentication-and-asp-net-core-integration-testing-using-testserver-15d47b03045a'
    Client.DefaultRequestHeaders.Add("my-name", "admin");
    Client.DefaultRequestHeaders.Add("my-id", "2");

    // Act
    var response = await GetResponseAsStringAsync(
        GetUrl<HomeController>(nameof(HomeController.Index))
    );

    // Assert
    response.ShouldNotBeNullOrEmpty();
}

引用here的博客允许我使用ClaimsPrincipal类向ClaimsIdentity提供Middleware。中间件类如下:

public class TestAuthenticationMiddleware
{
    private readonly RequestDelegate _next;

    public TestAuthenticationMiddleware(RequestDelegate next, IAuthenticationSchemeProvider schemas)
    {
        _next = next;
    }

    public async Task Invoke(HttpContext context)
    {
        if (context.Request.Headers.Keys.Contains("my-name"))
        {
            if (context.Request.Headers["my-name"].First().Equals("admin"))
            {
                ClaimsIdentity claimsIdentity = new ClaimsIdentity(new List<Claim>
                    {
                        new Claim(ClaimTypes.Name, "admin"),
                        new Claim(ClaimTypes.NameIdentifier, context.Request.Headers["my-id"].First()),
                        new Claim(ClaimTypes.Role, "Admin"),
                        new Claim("http://www.aspnetboilerplate.com/identity/claims/tenantId", "1", "int"),
                        new Claim("AspNet.Identity.SecurityStamp", Guid.NewGuid().ToString())

                    },
                    "Identity.Application");

                ClaimsPrincipal principal = new ClaimsPrincipal(claimsIdentity);
                context.User = principal;
                await context.SignInAsync("Identity.Application", principal);
            }
        }

        await _next(context);
    }
}

因此,运行测试时得到的结果是失败的测试。

Shouldly.ShouldAssertException : response.StatusCode
    should be
HttpStatusCode.OK
    but was HttpStatusCode.Redirect

我认为正在发生的事情是,我们被困在[AbpMvcAuthorize]功能中,并被重定向到登录页面。如果我从控制器中删除了AbpMvcAuthorize,那么我将得到一个不同的失败状态。我收到空引用错误。 View尝试渲染时,在调用GetShownLoginName()的后续视图模型中失败:

public class SideBarUserAreaViewModel
{
    public GetCurrentLoginInformationsOutput LoginInformations { get; set; }

    public bool IsMultiTenancyEnabled { get; set; }

    public string GetShownLoginName()
    {
        var userName = "<span id=\"HeaderCurrentUserName\">" + LoginInformations.User.UserName + "</span>";

        if (!IsMultiTenancyEnabled)
        {
            return userName;
        }

        return LoginInformations.Tenant == null
            ? ".\\" + userName
            : LoginInformations.Tenant.TenancyName + "\\" + userName;
    }
}

我希望能够测试我的控制器逻辑,以确保对视图的更改,对视图模型的更改以及对服务的更改不会无意中导致页面加载错误。除了在我的UserManager类中创建LogInManagerSignInManagerTestBase的实例并以编程方式登录用户之外,还有什么方法?

1 个答案:

答案 0 :(得分:2)

问题

  1. 您的MyProjectWebTestBase继承了AbpAspNetCoreIntegratedTestBase<TStartup>
  2. AbpAspNetCoreIntegratedTestBase<TStartup>使用TestAbpSession
  3. TestAbpSession忽略声明。

解决方案

  1. 实施MyTestAbpSession,而后退ClaimsAbpSession

    public class MyTestAbpSession : TestAbpSession
    {
        public ClaimsAbpSession ClaimsAbpSession { get; set; }
    
        public MyTestAbpSession(IMultiTenancyConfig multiTenancy,
            IAmbientScopeProvider<SessionOverride> sessionOverrideScopeProvider,
            ITenantResolver tenantResolver)
            : base(multiTenancy, sessionOverrideScopeProvider, tenantResolver)
        {
        }
    
        public override long? UserId
        {
            get => base.UserId ?? ClaimsAbpSession.UserId; // Fallback
            set => base.UserId = value;
        }
    }
    
  2. 使用您的PreInitialize的{​​{1}}方法进行注册。

    MyProjectWebTestModule