ASP.NET Core 6 MVC集成测试 - 授权

5
这是我的问题。我似乎无法创建需要已认证用户的集成测试。我使用 Microsoft.AspNetCore.Mvc.Testing 进行测试。以下是我的测试:

enter image description here

如您所见,客户端拥有“患者”角色和一个用户ID。以下是我的助手:

public class TestClaimsProvider
{
        public IList<Claim> Claims { get; }

        public TestClaimsProvider(IList<Claim> claims)
        {
            Claims = claims;
        }

        public TestClaimsProvider()
        {
            Claims = new List<Claim>();
        }

        public static TestClaimsProvider WithAdminClaims()
        {
            var provider = new TestClaimsProvider();
            provider.Claims.Add(new Claim(ClaimTypes.NameIdentifier, Guid.NewGuid().ToString()));
            provider.Claims.Add(new Claim(ClaimTypes.Name, "Admin user"));
            provider.Claims.Add(new Claim(ClaimTypes.Role, "Administrator"));

            return provider;
        }

        public static TestClaimsProvider WithUserClaims()
        {
            var provider = new TestClaimsProvider();
            provider.Claims.Add(new Claim(ClaimTypes.NameIdentifier, Guid.NewGuid().ToString()));
            provider.Claims.Add(new Claim(ClaimTypes.Name, "Patient"));
            provider.Claims.Add(new Claim(ClaimTypes.Role, "Patient"));

            return provider;
        }
}

这也包括:
public class TestAuthHandler : AuthenticationHandler<AuthenticationSchemeOptions>
{
    private readonly IList<Claim> _claims;

    public TestAuthHandler(IOptionsMonitor<AuthenticationSchemeOptions> options, 
            ILoggerFactory logger, 
            UrlEncoder encoder, 
            ISystemClock clock, TestClaimsProvider claimsProvider) : base(options, logger, encoder, clock)
    {
        _claims = claimsProvider.Claims;
    }

    protected override Task<AuthenticateResult> HandleAuthenticateAsync()
    {
        var identity = new ClaimsIdentity(_claims, "Test");
        var principal = new ClaimsPrincipal(identity);
        var ticket = new AuthenticationTicket(principal, "Test");

        var result = AuthenticateResult.Success(ticket);

        return Task.FromResult(result);
    }
}

而且:

public static class WebApplicationFactoryExtensions
{
    public static WebApplicationFactory<T> WithAuthentication<T>(this WebApplicationFactory<T> factory, TestClaimsProvider claimsProvider) where T : class
    {
        return factory.WithWebHostBuilder(builder =>
        {
            builder.ConfigureTestServices(services =>
            {
                services.AddAuthentication("Test")
                        .AddScheme<AuthenticationSchemeOptions, TestAuthHandler>("Test", op => { });

            services.AddScoped<TestClaimsProvider>(_ => claimsProvider);
        });
    });
}

public static HttpClient CreateClientWithTestAuth<T>(this WebApplicationFactory<T> factory, TestClaimsProvider claimsProvider) where T : class
{
    var client = factory.WithAuthentication(claimsProvider).CreateClient(new WebApplicationFactoryClientOptions
    {
        AllowAutoRedirect = false
    });

    client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Test");

    return client;
}

这些基于https://gunnarpeipman.com/aspnet-core-integration-tests-users-roles/主题。尽管我没有使用他在之前线程中指出的FakeStartup类,但是我已经尝试了这里https://learn.microsoft.com/en-us/aspnet/core/test/integration-tests?view=aspnetcore-6.0#customize-the-client-with-withwebhostbuilder文档中的身份验证,但是结果相同。
这是我在控制器中的操作方法:
[Authorize(Roles = PatientRoleName)]
public async Task<IActionResult> MakePatientAppointment()
{
    var patient = await this.patientService.GetPatientByUserIdAsync(this.User.GetId());

    if (string.IsNullOrWhiteSpace(patient.FirstName) || 
        string.IsNullOrWhiteSpace(patient.LastName) || 
        string.IsNullOrWhiteSpace(patient.Phone))
    {
        this.TempData["Message"] = PatientProfileIsNotFinishedMsg;

        return RedirectToAction("Finish", "Patient", new { area = "" });
    }

    var viewModel = new PatientAppointmentCreateModel
            {
                DoctorId = await this.doctorService.GetDoctorId(),
                AppointmentCauses = await this.appointmentCauseService.GetAllCauses()
            };

    return View(viewModel); 
}

从调试测试的结果来看,响应重定向到 /Identity/Login,因此我理解用户尚未登录。我该如何重构代码以使用户得到认证?

这个回答解决了你的问题吗?如何为集成测试模拟Jwt承载令牌 - S. ten Brinke
1个回答

12

针对 ASP.net 6 进行了更新

C# 10 的新特性 顶层语句,将StartupProgram 类统一为一个单一的 Program 类,因此(除了新的 最小托管模型之外),创建 WebApplicationFactory 的方式也发生了改变。从如何将Program类的可见性更改为 TestFixture开始(因为由于顶层语句功能而不再包含命名空间)...

文档已经做出了更新,但是,我们注册 AuthenticationHandler 来创建授权模拟客户端的方法也发生了改变——但是文档(至少目前为止)并未解决这个问题。

因此,如果我们像在 ASP.net 5 中那样注册 AuthenticationHandler,现在在 ASP.net 6 中会收到 401 未经授权的错误(因为此方法不再起作用)。

经过很多研究,我找到了解决方案:

现在,必须根据 WebApi 中的设置,在 WebApplicationFactory 中显式设置 AuthenticationOptions 设置,例如我这里的设置:

{
    o.DefaultAuthenticateScheme = "Test";
    o.DefaultChallengeScheme = "Test";
}

enter image description here


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接