Интеграционный тест ASP.NET Core 7 с клиентом CustomWebApplicationFactory<TPgram> всегда возвращает 404, не найдено
У меня есть мойasp.net core 7 web api
приложение работает с использованием и . У меня есть мойIntegration Tests
уже написано с использованиемCustomWebApplicationFactory<TStartup>
. Все работает как положено.
Теперь я решил отойти от . Итак, я переместил всеStartup.cs
логика внутри. МойProgram.cs
выглядит как,
try
{
//Read Configuration from appSettings
var config = new ConfigurationBuilder()
.AddJsonFile("appsettings.json")
.Build();
//Initialize Logger
Log.Logger = new LoggerConfiguration()
.ReadFrom.Configuration(config)
.CreateLogger();
Log.Information($"Starting {typeof(Program).Assembly.FullName}");
var builder = WebApplication.CreateBuilder();
builder.Host.UseSerilog();//Uses Serilog instead of default .NET Logger
builder.WebHost.ConfigureKestrel(options =>
{
// Set properties and call methods on options
options.AddServerHeader = false;
});
JwtSecurityTokenHandler.DefaultInboundClaimTypeMap.Clear();
JwtSecurityTokenHandler.DefaultInboundClaimTypeMap.Add("sub", ClaimTypes.NameIdentifier);
//JwtSecurityTokenHandler.DefaultInboundClaimTypeMap.Add("role", ClaimTypes.Role);
builder.Services.AddHttpContextAccessor()
.AddApiClients(builder.Configuration)
.AddApplicationServices(builder.Configuration)
.AddMemoryCache()
.AddResponseCompression()
.AddApiControllersAndBehavior()
.AddApiVersion()
.AddApiAuthenticationAndAuthorization(builder.Configuration)
.AddSwagger(builder.Configuration)
.AddApplicationServices()
.AddCors(options =>
{
options.AddPolicy("AppClients", policyBuilder => policyBuilder.WithOrigins(builder.Configuration.GetValue<string>("WebClient"))
.AllowAnyHeader()
.AllowAnyMethod());
})
//.AddHttpLogging(options =>
//{
// options.LoggingFields = HttpLoggingFields.All;
//})
.AddFeatureManagement()
.UseDisabledFeaturesHandler(new DisabledFeatureHandler());
var consoleLogging = new ConsoleLogging(builder.Configuration.GetValue<bool>("EnableConsoleLogging"));
builder.Services.AddSingleton(consoleLogging);
var commandsConnectionString = new CommandConnectionString(builder.Configuration.GetConnectionString("CommandsConnectionString"));
builder.Services.AddSingleton(commandsConnectionString);
var queriesConnectionString = new QueryConnectionString(builder.Configuration.GetConnectionString("QueriesConnectionString"));
builder.Services.AddSingleton(queriesConnectionString);
if (builder.Environment.IsDevelopment())
{
//builder.Services.AddScoped<BaseReadContext, AppInMemoryReadContext>();
//builder.Services.AddScoped<BaseContext, AppInMemoryContext>();
builder.Services.AddScoped<BaseReadContext, AppSqlServerReadContext>();
builder.Services.AddScoped<BaseContext, AppSqlServerContext>();
builder.Services.AddMiniProfilerServices();
}
else
{
builder.Services.AddScoped<BaseReadContext, AppSqlServerReadContext>();
builder.Services.AddScoped<BaseContext, AppSqlServerContext>();
}
var app = builder.Build();
app.UseMiddleware<ExceptionHandler>()
//.UseHttpLogging()
.UseSecurityHeaders(SecurityHeadersDefinitions.GetHeaderPolicyCollection(builder.Environment.IsDevelopment()))
.UseHttpsRedirection()
.UseResponseCompression();
if (builder.Environment.IsDevelopment())
{
app.UseMiniProfiler()
.UseSwagger()
.UseSwaggerUI(options =>
{
foreach (var description in app.Services.GetRequiredService<IApiVersionDescriptionProvider>().ApiVersionDescriptions)
{
options.SwaggerEndpoint(
$"swagger/AppOpenAPISpecification{description.GroupName}/swagger.json",
$"App API - {description.GroupName.ToUpperInvariant()}");
}
options.OAuthClientId("appswaggerclient");
options.OAuthAppName("App API");
options.OAuthUsePkce();
options.RoutePrefix = string.Empty;
options.DefaultModelExpandDepth(2);
options.DefaultModelRendering(ModelRendering.Model);
options.DocExpansion(DocExpansion.None);
options.DisplayRequestDuration();
options.EnableValidator();
options.EnableFilter();
options.EnableDeepLinking();
options.DisplayOperationId();
});
}
app.UseRouting()
.UseCors("AppClients")
.UseAuthentication()
.UseAuthorization()
.UseRequestLocalization(options =>
{
var supportedCultures = new[] { "en", "en-IN", "en-US" };
options.SetDefaultCulture("en-IN");
options.AddSupportedCultures(supportedCultures);
options.AddSupportedUICultures(supportedCultures);
options.ApplyCurrentCultureToResponseHeaders = true;
})
.UseEndpoints(endpoints =>
{
endpoints.MapControllers();
});
await app.RunAsync();
}
catch (Exception ex)
{
Log.Fatal(ex, "The Application failed to start.");
}
finally
{
Log.CloseAndFlush();
}
/// <summary>
/// Added to Make FunctionalTest Compile
/// </summary>
public partial class Program { }
Вот мойCustomWebApplicationFactory<TProgram>
,
public class CustomWebApplicationFactory<TProgram> : WebApplicationFactory<TProgram> where TProgram : class
{
protected override void ConfigureWebHost(IWebHostBuilder builder)
{
var projectDir = Directory.GetCurrentDirectory();
builder.ConfigureAppConfiguration((context, conf) =>
{
conf.AddJsonFile(Path.Combine(projectDir, "appsettings.Test.json"));
});
builder.UseEnvironment("Testing");
builder.ConfigureTestServices(async services =>
{
services.AddAuthentication("Test")
.AddScheme<AuthenticationSchemeOptions, TestAuthHandler>("Test", options => { });
services.AddScoped(_ => AuthClaimsProvider.WithMasterClaims());
var descriptor = services.SingleOrDefault(d => d.ServiceType == typeof(BaseContext));
if (descriptor != null)
{
services.Remove(descriptor);
}
descriptor = services.SingleOrDefault(d => d.ServiceType == typeof(BaseReadContext));
if (descriptor != null)
{
services.Remove(descriptor);
}
descriptor = services.SingleOrDefault(d => d.ServiceType == typeof(ITenantService));
if (descriptor != null)
{
services.Remove(descriptor);
services.AddTransient<ITenantService, TestTenantService>();
}
var connectionStringBuilder = new SqliteConnectionStringBuilder { DataSource = ":memory:" };
var connection = new SqliteConnection(connectionStringBuilder.ToString());
var dbContextOptions = new DbContextOptionsBuilder<AppSqliteInMemoryContext>()
.UseSqlite(connection)
.Options;
services.AddScoped<BaseContext>(options => new AppSqliteInMemoryContext(dbContextOptions));
var dbContextReadOptions = new DbContextOptionsBuilder<AppSqliteInMemoryReadContext>()
.UseSqlite(connection)
.Options;
services.AddScoped<BaseReadContext>(options => new AppSqliteInMemoryReadContext(
dbContextReadOptions, options.GetRequiredService<ITenantService>()));
await connection.CloseAsync();
var sp = services.BuildServiceProvider();
using var scope = sp.CreateScope();
var scopedServices = scope.ServiceProvider;
var db = scopedServices.GetRequiredService<BaseContext>();
var logger = scopedServices.GetRequiredService<ILogger<CustomWebApplicationFactory<Program>>>();
try
{
await db.Database.OpenConnectionAsync();
await db.Database.EnsureCreatedAsync();
await DatabaseHelper.InitialiseDbForTests(db);
}
catch (Exception ex)
{
logger.LogError(ex, $"An error occurred seeding the database with test data. Error: {ex.Message}");
throw;
}
});
}
}
Вот мойIntegration Test
,
public class GetByIdTests : IClassFixture<CustomWebApplicationFactory<Program>>
{
private readonly HttpClient _client;
public GetByIdTests(CustomWebApplicationFactory<Program> factory)
{
//factory.ClientOptions.BaseAddress = new Uri("https://localhost:44367");
_client = factory.CreateClient(new WebApplicationFactoryClientOptions
{
BaseAddress = new Uri("https://localhost:44367"),
AllowAutoRedirect = false
});
_client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Test");
_client.DefaultRequestHeaders.Add("x-api-version", "1.0");
}
[Fact]
public async Task GetById_ReturnsExpectedResponse_ForMasterUser()
{
var id = Guid.Parse("6B4DFE8A-2FCB-4716-94ED-4D63BF9351C6");
using var request = new HttpRequestMessage(HttpMethod.Get, $"/api/branches/{id}");
var response = await _client.SendAsync(request, HttpCompletionOption.ResponseHeadersRead);
}
}
Я выполнил все шаги, указанные в официальных документах . Однако, когда я запускаю тест, я продолжаю получать 404 не найдено.
Вот скриншот ошибки,
Пожалуйста, не могли бы вы помочь мне, что я делаю неправильно?
2 ответа
Это движение отStartup.cs
to был в моем отставании в течение долгого времени, и каждый раз, когда я пытался, я заканчивал404
NotFound
в тестах.
Наконец я понял. Вот как.
Я сравнивал свою строку за строкой с eshoponweb и заметил, что мне не хватает в моемCreateBuilder()
.
В моем я только что изменился с этого
var builder = WebApplication.CreateBuilder();
к
var builder = WebApplication.CreateBuilder(args);
// здесь добавлены аргументы
и он начал работать.
Причина мояProgram.cs
отсутствовало то, что нашsonar qube
сканер выделялsecurity risk
дляargs
и поэтому мы удалили это, когда наш проект был нацелен наASP.NET Core 3.1
и теперь это привело к тому, что наш тест провалился, когда проект нацеленASP.NET Core 6
или выше.
В своем классе интеграционного теста вы настраиваете HttpClient с явным BaseAddress.
_client = factory.CreateClient(new WebApplicationFactoryClientOptions
{
BaseAddress = new Uri("https://localhost:44367"),
AllowAutoRedirect = false
});
Но, насколько я вижу, вы не предоставляете эту конфигурацию в своей CustomWebApplicationFactory. Чтобы сделать это программно без переменной среды, вы можете вызвать метод UseUrls() в IHostWebApplicationBuilder.
builder.UseUrls("http://localhost:44367");