如何在集成测试中从服务中删除 AuthorizeAreaFolder 选项

问题描述

我使用自定义 WebApplicationFactory 对我的 razor Web 应用程序进行了集成测试:

public class CustomWebApplicationFactory<TStartup>
    : WebApplicationFactory<TStartup> where TStartup : class
{
    protected override void ConfigureWebHost(IWebHostBuilder builder)
    {
        builder.ConfigureServices(services =>
        {
            var descriptor = services.SingleOrDefault(
                d => d.ServiceType ==
                    typeof(DbContextOptions<GenericDbContext>));

            services.Remove(descriptor);


            services.AddDbContext<GenericDbContext>(options =>
            {
                options.UseInMemoryDatabase("InMemoryDbForTesting");
            });

            var sp = services.BuildServiceProvider();

            using var scope = sp.CreateScope();

            var scopedServices = scope.ServiceProvider;
            var db = scopedServices.GetrequiredService<GenericDbContext>();
            var logger = scopedServices
                .GetrequiredService<ILogger<CustomWebApplicationFactory<TStartup>>>();

            db.Database.EnsureCreated();

            try
            {
                Utilities.InitializeDbForTests(db);
            }
            catch (Exception ex)
            {
                logger.LogError(ex,"An error occurred seeding the " +
                    "database with test messages. Error: {Message}",ex.Message);
            }
        });
    }
}

在 TStartup 中,我传递了这个启动配置服务:

public void ConfigureServices(IServiceCollection services)
{
    services.AddSingleton<IHttpContextAccessor,HttpContextAccessor>();

    //AzureADB2C
    services.AddAuthentication(sharedOptions =>
    {
        sharedOptions.DefaultScheme = CookieAuthenticationDefaults.AuthenticationScheme;
        sharedOptions.DefaultChallengeScheme = OpenIdConnectDefaults.AuthenticationScheme;
    })
    
    .AddAzureAdB2C(options => Configuration.Bind("Authentication:AzureAdB2C",options))
    .AddCookie(options =>
    {
        options.Cookie.SameSite = SameSiteMode.None;
        options.Cookie.SecurePolicy = CookieSecurePolicy.Always;
        options.Cookie.IsEssential = true;
        //options.ExpireTimeSpan = TimeSpan.FromDays(365);
    });
    //AzureADB2C

    //Simple
    //services.AddAuthentication(AzureADB2CDefaults.AuthenticationScheme)
    //    .AddAzureADB2C(options => Configuration.Bind("Authentication:AzureAdB2C",options));


    services.Configure<CookiePolicyOptions>(options =>
    {
        // This lambda determines whether user consent for non-essential cookies is needed for a given request.
        options.CheckConsentNeeded = context => false; // = context => true; //When set to true,TempData will not work unless the user opts in.
        options.MinimumSameSitePolicy = SameSiteMode.Unspecified; //If we dont do the hack for chrome/safari,this should be set to 'None'
        options.Secure = CookieSecurePolicy.Always;

        //Hack for Chrome/Safari. May not be needed in future
        options.OnAppendCookie = cookieContext =>
            CheckSameSite(cookieContext.Context,cookieContext.CookieOptions);
        options.OnDeleteCookie = cookieContext =>
           CheckSameSite(cookieContext.Context,cookieContext.CookieOptions);
    });

    services.AddRouting(options => {
        options.LowercaseUrls = true;
        //options.AppendTrailingSlash = false;
        });


 
    //Response Caching
    services.AddResponseCaching();

    //With conditional runtime compilation
    IMvcBuilder builder = services.AddRazorPages()
        .AddRazorPagesOptions(options =>
        {
            //Sitemap
            options.Conventions.AddAreaPageRoute("xxxxxx","/xxxxxx","xxxxxx.xml");
            options.Conventions.AddAreaPageRoute("xxxxxx","xxxxxx/xxxxxx.xml");
            options.Conventions.AddAreaPageRoute("xxxxxx","xxxxxx/xxxxxx.xml");



            options.Conventions.AuthorizeAreaFolder("xxxxxx","/");
            options.Conventions.AuthorizeAreaFolder("xxxxxx","/");
            options.Conventions.AuthorizeAreaFolder("xxxxxxx","/","xxxxx");
            //options.Conventions.AuthorizeAreaFolder("xxxx","/");
            //options.Conventions.AuthorizeAreaFolder("xxxxx","/");
            options.Conventions.AuthorizeAreaPage("xxxx","/xxxxx");


            //password reset
            options.Conventions.AllowAnonymousToAreaPage("xxxxx","/xxxxx");
            options.Conventions.AllowAnonymousToAreaPage("xxxxx","/xxxx");

            //Checkout and 3DS pages
            options.Conventions.AllowAnonymousToAreaPage("xxxxx","/xxxxx");


        });

    services.AddMemoryCache();

    //CACHE for tokens
    // Adds a default in-memory implementation of IdistributedCache.
    services.AdddistributedMemoryCache();
    //services.AddSession();

    services.AddSession(options =>
    {
        options.IdleTimeout = TimeSpan.FromHours(1);
        options.Cookie.HttpOnly = true;
        options.Cookie.IsEssential = true;
        options.Cookie.SecurePolicy = CookieSecurePolicy.Always;
        options.Cookie.SameSite = SameSiteMode.Unspecified;// SameSiteMode.None;
        //options.IdleTimeout = TimeSpan.FromDays(365);
    });


    services.AddDbContext<IGenericDbContext,GenericDbContext>(options => options.UsesqlServer(Configuration.GetConnectionString("GenericDbConnection")));
}

就像我在自定义 WebApplicationFactory 中删除 dbContextOption 一样,我想删除 AuthorizeAreaFolder 或覆盖“services.AddRazorPages().AddRazorPagesOptions()”以允许集成测试在不伪造授权的情况下获取或发布页面

解决方法

AddRazorPagesOptions 本质上只是在服务集合上配置了 RazorPagesOptions,所以它等价于如下:

services.Configure<RazorPagesOptions>(options =>
{
    options.Conventions.AuthorizeAreaFolder("xxxxxx","/");
    // …
});

这也意味着为了改变约定,你可以重新配置RazorPagesOptions,直接修改约定集合。不幸的是,IPageConvention 类型是私有的,因此您无法直接查找由 AuthorizeAreaFolder 添加的条目并使用 RemoveType 方法删除它们。但是,您可以clear 整个集合以完全删除所有约定:

services.Configure<RazorPagesOptions>(options =>
{
    options.Conventions.Clear();
});