ASP.NET Core 7与CustomWebApplicationFactory客户端的集成测试< TProgram>始终返回404找不到

fquxozlt  于 2023-01-03  发布在  .NET
关注(0)|答案(2)|浏览(122)

我使用Program.csStartup.cs运行我的asp.net core 7 web api应用程序。我已经使用CustomWebApplicationFactory<TStartup>编写了我的Integration Tests。所有都按预期工作。
现在我决定放弃Startup.cs,所以我把所有的Startup.cs逻辑都移到了Program.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);
    }
}

我已经按照official docs中提到的所有步骤进行了测试。但是当我运行测试时,我一直得到404未找到。
这是错误屏幕截图,

你能帮我看看我做错了什么吗?

wj8zmpe1

wj8zmpe11#

在集成测试类中,您使用显式BaseAddress配置HttpClient。

_client = factory.CreateClient(new WebApplicationFactoryClientOptions
    {
        BaseAddress = new Uri("https://localhost:44367"),
        AllowAutoRedirect = false
    });

但据我所知,您没有在CustomWebApplicationFactory中提供此配置。要在没有环境变量的情况下以编程方式执行此操作,您可以调用IHostWebAplicationBuilder上的UseUrls()方法

builder.UseUrls("http://localhost:44367");
k4ymrczo

k4ymrczo2#

Startup.csProgram.cs的这种移动在我的积压工作中已经有很长时间了,每次我尝试的时候,我在测试中都以404NotFound结束。
最后我想明白了。这就是办法。
我正在将我的Program.cseshoponwebProgram.cs逐行比较,并注意到我的CreateBuilder()中缺少args
在我的Program.cs中,我只是将
var builder = WebApplication.CreateBuilder();

var builder = WebApplication.CreateBuilder(args);//在此处添加了参数
它开始起作用了。
我的Program.cs缺少args的原因是我们的sonar qube扫描仪突出显示了argssecurity risk,因此我们在项目针对ASP.NET Core 3.1时删除了它,现在当项目针对ASP.NET Core 6或更高版本时,它使我们的测试失败。

相关问题