如何覆盖WebApplicationFactory中的连接字符串?

问题描述 投票:0回答:1

我有一个 API,我想对其进行集成测试。我有一个 Program.cs 和一个定制的 Startup.cs,它基本上是一个静态类,其中包含

WebApplicationBuilder
的扩展方法,以模仿 .NET 6 之前的 .NET 行为。

// Program.cs
WebApplication.CreateBuilder(args)
    .ConfigureLogging()
    .ConfigureServices()
    .Build()
    .Initialize()
    .ConfigurePipeline()
    .Run();

// Startup.cs
public static class Startup
{
    public static WebApplicationBuilder ConfigureLogging(this WebApplicationBuilder builder)
    {
        // Configure logging
        return builder;
    }

    public static WebApplicationBuilder ConfigureServices(this WebApplicationBuilder builder)
    {
        var services = builder.Services;
        var configuration = builder.Configuration;

        services.AddPooledDbContextFactory<AppDbContext>((serviceProvider, options) =>
        {
            var connectionString = configuration.GetConnectionString("Database");
            var dataSourceBuilder = new NpgsqlDataSourceBuilder(connectionString);
            dataSourceBuilder.EnableDynamicJson();
            options.UseNpgsql(dataSourceBuilder.Build());
        });

        return builder;
    }

    public static WebApplication ConfigurePipeline(this WebApplication app)
    {
        // Register middleware
        app.UseRouting();
        app.MapControllers();
        return app;
    }

    public static WebApplication Initialize(this WebApplication app)
    {
        // Resolve DbContext and call .Migrate()
        return app;
    }
}

我想做的是创建一个自定义

WebApplicationFactory<>
并覆盖其中的一些内容,例如将使用的数据库。我正在使用 Testcontainers.PostgreSql NuGet 包,我的工厂如下所示:

public class MyFactory : WebApplicationFactory<IAssemblyMarker>, IAsyncLifetime
{
    private readonly PostgreSqlContainer _postgreSqlContainer = new PostgreSqlBuilder()
        .WithUsername("username")
        .WithPassword("password")
        .WithDatabase("mydb")
        .Build();

    protected override void ConfigureWebHost(IWebHostBuilder builder)
    {
        builder.ConfigureLogging(logging =>
        {
            logging.ClearProviders();
        });

        // If I use ConfigureTestServices, the test first calls ConfigureTestServices,
        // then ConfigureServices, and then Startup.ConfigureServices (the extension method)
        //builder.ConfigureTestServices(services =>
        builder.ConfigureServices(services =>
        {
            var connectionString = _postgreSqlContainer.GetConnectionString();

            services.RemoveAll<AppDbContext>();
            services.RemoveAll<IDbContextFactory<AppDbContext>>();
            services.RemoveAll<IDataTypeBuilder>();

            services.AddSingleton<IDataTypeBuilder, NpgsqlDataTypeBuilder>();
            services.AddPooledDbContextFactory<AppDbContext>(options =>
            {
                var dataSourceBuilder = new NpgsqlDataSourceBuilder(connectionString)
                {
                    Name = "IntegrationTests App"
                };
                options.UseNpgsql(dataSourceBuilder.Build());
            });
        });
    }

    public async Task InitializeAsync()
    {
        await _postgreSqlContainer.StartAsync();
    }

    async Task IAsyncLifetime.DisposeAsync()
    {
        await _postgreSqlContainer.StopAsync();
    }
}

问题是,调用

_myFactory.CreateClient()
总是会抛出异常,因为我的
appsettings.json
中的连接字符串是为我的本地 PostgreSQL 容器定义的,这自然会导致集成测试失败。我确实尝试设置断点,但有一个问题:为什么我的测试方法(调用 CreateClient 之后)输入
WebApplicationFactory<>.ConfigureServices
(或ConfigureTestServices - 我似乎找不到两者之间的区别),然后是
Startup.ConfigureServices 
?然后发生的情况是,
PostgreSqlContainer
连接字符串在我的测试中根本不起作用,但测试方法尝试连接到我的
appsettings.json
中定义的数据库。

正确的修复和解决方法是什么?自定义 Startup 类可能是问题所在(因为

Startup.ConfigureServices
是自定义扩展方法)?

.net testing integration-testing xunit .net-8.0
1个回答
0
投票

所以,我设法找到了一个“解决方法”(不过不知道是否应该这样做)。我相信因为我的

AppDbContext
是使用
AddPooledDbContextFactory<AppDbContext>
注册的,所以我还没有在
services.RemoveAll<>
/
WebApplicationFactory.ConfigureWebHost
内的
builder.ConfigureTestServices
中删除使用
builder.ConfigureServices
的所有服务注册(我仍然不知道两者之间有什么区别)会是),所以
AppDbContext
是在我的自定义
Startup
类中使用预先存在的 appsettings.json 配置注册的,之后,我尝试在我的
DbContextFactory
中重新注册
MyFactory

所以,到目前为止,我的解决方法是从我的

ConfigureTestServices
中完全删除
ConfigureServices
MyFactory : WebApplicationFactory
并调用
ConfigureAppConfiguration
,这允许我删除任何
IConfiguration
源并定义我自己的
Dictionary<>
,这将有我的自定义应用程序配置的(集成测试特定)值。

builder.ConfigureAppConfiguration((ctx, builder) =>
{
    var configuration = new Dictionary<string, string?>
    {
        ["ConnectionStrings:Database"] = _postgreSqlContainer.GetConnectionString(),
    };

    builder.Sources.Clear();
    builder.AddInMemoryCollection(configuration);
});

这样做的结果是,当我的

Startup.ConfigureServices
被我的
Program
类调用时,它使用的
IConfiguration
只有我上面定义的键值对,所以它尝试使用的连接字符串基本上是由我的集成测试预先设置的
MyFactory

我会将此问题标记为已回答。如果将来有人有更好的答案或解释,请随时发布在这里。

© www.soinside.com 2019 - 2024. All rights reserved.