如何结合WebApplicationFactory与Hangfire开展集成测试?
兄弟,我之前做集成测试搭环境的时候,也碰到过Hangfire配PostgreSQL踩这个坑的情况,你这个ArgumentException: Couldn't set user的错误我太熟了!咱们一步步来捋清楚问题在哪,怎么解决。
首先先明确你的场景:你想用Test Containers启动真实的PostgreSQL实例,再配合WebApplicationFactory搭集成测试环境,结果给Hangfire配置PostgreSQL存储的时候栽了跟头。这个错误大概率是连接字符串、配置时机或者包版本的问题,咱们一个个排查。
第一步:先确保Test Containers的Postgres实例真的就绪了
你得先保证测试用的Postgres容器完全启动,拿到的连接字符串是有效的。比如在测试类里这么写初始化代码,别让容器还没起来就去拿连接字符串:
// 测试类里的容器初始化示例(用NUnit的写法,xUnit可以用ICollectionFixture实现) private readonly PostgreSqlContainer _postgresContainer = new PostgreSqlBuilder() .WithImage("postgres:15-alpine") // 选个稳定的镜像版本 .WithUsername("test_user") // 手动指定用户,避免默认值出问题 .WithPassword("test_pwd") .WithDatabase("test_db") .Build(); [OneTimeSetUp] public async Task Setup() { // 一定要等容器完全启动再继续,别着急拿连接串 await _postgresContainer.StartAsync(); } [OneTimeTearDown] public async Task TearDown() { await _postgresContainer.StopAsync(); }
第二步:在WebApplicationFactory里正确覆盖Hangfire的配置
你原来的项目里肯定已经有Hangfire的配置了,在测试环境里得把它替换成测试用的Postgres配置,别让生产的配置串过来。在自定义的WebApplicationFactory里这么改:
public class TestWebAppFactory<TProgram> : WebApplicationFactory<TProgram> where TProgram : class { public string PostgresConnStr { get; set; } protected override void ConfigureWebHost(IWebHostBuilder builder) { builder.ConfigureServices(services => { // 先把原来注册的Hangfire相关服务清掉,避免冲突 var hangfireDescriptors = services.Where(d => d.ServiceType.Namespace?.StartsWith("Hangfire") == true).ToList(); foreach (var descriptor in hangfireDescriptors) { services.Remove(descriptor); } // 重新配置Hangfire用测试容器的Postgres services.AddHangfire(config => { config.UsePostgreSqlStorage(c => { c.UseNpgsqlConnection(PostgresConnStr); // 可以加个测试专用的Schema,避免和其他环境混 c.UseSchemaName("hangfire_test"); // 自签名证书的容器一定要加这个参数,不然会有SSL错误间接导致用户参数问题 c.UseNpgsqlConnection(builder => builder.TrustServerCertificate(true)); }); }); // 要是测试需要跑后台任务就注册Hangfire Server,不需要的话可以注释掉 services.AddHangfireServer(); }); } }
第三步:排查连接字符串的坑
你那个“Couldn't set user”的错误,90%是连接字符串的问题。先把容器生成的连接字符串打出来看看,确认里面的Username、Password参数都对:
// 在测试方法里加一行,输出连接字符串核对 Console.WriteLine($"当前Postgres连接串:{_postgresContainer.GetConnectionString()}");
正确的连接串格式应该是这样的:Host=localhost;Port=5432;Database=test_db;Username=test_user;Password=test_pwd;Trust Server Certificate=true
要是发现缺了Username或者Password,那就是容器初始化的时候没指定,或者Test Containers的版本问题,手动补全就行。
第四步:检查包版本兼容性
有时候Hangfire.PostgreSql和Npgsql的版本不兼容也会出奇怪的错误。比如Hangfire.PostgreSql 1.10.x需要搭配Npgsql 7.x,要是你用的Npgsql是6.x,就会有各种奇怪的参数错误。去NuGet包管理器里核对一下版本,尽量用官方推荐的兼容版本。
备选方案:测试时用内存存储(如果不需要测DB交互)
要是你只是想测试Web应用的其他逻辑,不需要验证Hangfire和Postgres的交互,那可以直接用Hangfire的内存存储,省得折腾数据库容器:
services.AddHangfire(config => config.UseMemoryStorage());
不过这个方案测试的真实性会差一点,适合快速验证业务逻辑,不适合测Hangfire的持久化场景。
备注:内容来源于stack exchange,提问作者Artyom Elfimov

