[英]ASP.NET Core 7 Integration Test with CustomWebApplicationFactory<TProgram> Client always returns 404 not found
I have my asp.net core 7 web api
app running using Program.cs
and Startup.cs
.我的
asp.net core 7 web api
应用程序正在使用Program.cs
和Startup.cs
运行。 I have my Integration Tests
already written using CustomWebApplicationFactory<TStartup>
.我已经使用
CustomWebApplicationFactory<TStartup>
编写了我的Integration Tests
。 All are working as expected.一切都按预期工作。
Now I have decided to move away from Startup.cs
.现在我决定离开
Startup.cs
。 So I have moved all Startup.cs
logics inside Program.cs
.所以我将所有
Startup.cs
逻辑都移到了Program.cs
中。 My Program.cs
looks like,我的
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 { }
Here is my CustomWebApplicationFactory<TProgram>
,这是我的
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;
}
});
}
}
Here is my Integration Test
,这是我的
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);
}
}
I have followed all the steps mentioned in official docs .我已经按照官方文档中提到的所有步骤进行操作。 However when I run the test I keep getting 404 not found.
但是,当我运行测试时,我一直找不到 404。
Here is the error screen shot,这是错误的屏幕截图,
Please can you assist me on what I'm doing wrong?请你能帮我解决我做错了什么吗?
In your integration test class you configure the HttpClient with an explicit BaseAddress.在您的集成测试 class 中,您使用显式 BaseAddress 配置 HttpClient。
_client = factory.CreateClient(new WebApplicationFactoryClientOptions
{
BaseAddress = new Uri("https://localhost:44367"),
AllowAutoRedirect = false
});
But as far as I can see, you don't provide this configutation in your CustomWebApplicationFactory.但据我所知,您没有在 CustomWebApplicationFactory 中提供此配置。 To do this programmatically without an environment variable you can call the UseUrls() method on the IHostWebApllicationBuilder
要在没有环境变量的情况下以编程方式执行此操作,您可以在 IHostWebApllicationBuilder 上调用 UseUrls() 方法
builder.UseUrls("http://localhost:44367");
This movement from Startup.cs
to Program.cs
was in my backlog for long time and every time I attempted I ended up with 404
NotFound
in the tests.从
Startup.cs
到Program.cs
的这种移动在我的积压工作中存在了很长时间,每次我尝试时,我都会在测试中以404
NotFound
告终。
Finally I figured out.最后我想通了。 Here is how.
方法如下。
I was comparing my Program.cs
line by line with eshoponweb Program.cs
and noticed that I was missing args
in my CreateBuilder()
.我逐行比较我的
Program.cs
和eshoponweb Program.cs
并注意到我在我的CreateBuilder()
中缺少args
。
In my Program.cs
, I just changed from this在我的
Program.cs
中,我只是从这个改变
var builder = WebApplication.CreateBuilder();
to到
var builder = WebApplication.CreateBuilder(args);
// added args here // 在这里添加参数
and it started working.它开始工作了。
The reason my Program.cs
was missing args
is that our sonar qube
scanner was highlighting a security risk
for args
and so we removed that when our project was targeting ASP.NET Core 3.1
and now that was making our test to fail when the project is targeting ASP.NET Core 6
or above.我的
Program.cs
缺少args
的原因是我们的sonar qube
扫描仪突出了args
的security risk
,因此当我们的项目以ASP.NET Core 3.1
为目标时,我们删除了它,现在当项目以ASP.NET Core 6
为目标时,这使我们的测试失败ASP.NET Core 6
或以上。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.