I have a integration test for my razor web application with custom WebApplicationFactory:
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);
}
});
}
}
In the TStartup I am passing this startup configure services:
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/xxxxxx.xml");
options.Conventions.AddAreaPageRoute("xxxxxx", "/xxxxxx", "xxxxxx/xxxxxx.xml");
options.Conventions.AddAreaPageRoute("xxxxxx", "/xxxxxx", "xxxxxx/xxxxxx.xml");
options.Conventions.AddAreaPageRoute("xxxxxx", "/xxxxxx", "xxxxxx/xxxxxx.xml");
options.Conventions.AddAreaPageRoute("xxxxxx", "/xxxxxx", "xxxxxx/xxxxxx.xml");
options.Conventions.AddAreaPageRoute("xxxxxx", "/xxxxxx", "xxxxxx/xxxxxx.xml");
options.Conventions.AuthorizeAreaFolder("xxxxxx", "/");
options.Conventions.AuthorizeAreaFolder("xxxxxx", "/");
options.Conventions.AuthorizeAreaFolder("xxxxxxx", "/", "xxxxx");
//options.Conventions.AuthorizeAreaFolder("xxxx", "/");
options.Conventions.AuthorizeAreaFolder("xxxxxx", "/");
options.Conventions.AuthorizeAreaFolder("xxxxxx", "/");
options.Conventions.AuthorizeAreaFolder("xxxxxx", "/");
//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")));
}
Like I am removing dbContextOption in the custom WebApplicationFactory, I want to remove AuthorizeAreaFolder or to override "services.AddRazorPages().AddRazorPagesOptions()" to allow integration test to get or post a page without faking authorization.
AddRazorPagesOptions
essentially just configures the RazorPagesOptions
on the service collection, so it is equivalent to the following:
services.Configure<RazorPagesOptions>(options =>
{
options.Conventions.AuthorizeAreaFolder("xxxxxx", "/");
// …
});
That also means that in order to change the conventions, you can reconfigure the RazorPagesOptions
and modify the conventions collection directly. Unfortunately, the IPageConvention
types are private, so you cannot look for the entries that are added by AuthorizeAreaFolder
directly and remove them with the RemoveType
method. However, you could clear the whole collection to remove all conventions completely:
services.Configure<RazorPagesOptions>(options =>
{
options.Conventions.Clear();
});