You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

.NET 6带测试服务器的E2E测试在TeamCity CI执行失败,本地手动运行正常求助

解决TeamCity CI中.NET 6 WebApplicationFactory真实端口绑定空地址问题

看起来你遇到的是TeamCity进程上下文里Kestrel服务器未成功绑定地址导致的空序列异常,手动执行正常但CI环境失败,大概率是进程启动时机或环境上下文的差异。下面是几个具体的排查和修复方案:

1. 给Kestrel地址添加等待与重试逻辑

TeamCity的进程调度可能比本地慢,导致你获取地址时服务器还没完成绑定。可以在获取地址前添加等待重试逻辑,确保Kestrel完全启动:

修改CreateHost方法中获取地址的部分:

// 替换原来的地址获取逻辑
var server = _host.Services.GetRequiredService<IServer>();
IServerAddressesFeature? addresses = null;

// 等待最多5秒,直到获取到绑定地址
var waitTime = TimeSpan.FromSeconds(5);
var startTime = DateTime.UtcNow;
do
{
    addresses = server.Features.Get<IServerAddressesFeature>();
    if (addresses?.Addresses?.Any() == true)
        break;
    Thread.Sleep(100);
} while (DateTime.UtcNow - startTime < waitTime);

if (addresses?.Addresses?.Any() != true)
{
    throw new InvalidOperationException("Kestrel server failed to bind to any address within the timeout period.");
}

ClientOptions.BaseAddress = addresses.Addresses
    .Select(x => new Uri(x))
    .Last();

2. 检查TeamCity环境变量是否干扰Kestrel

TeamCity可能会设置ASPNETCORE_URLS环境变量,覆盖你在UseUrls中的配置,导致Kestrel无法绑定地址。

排查步骤:

在TeamCity构建配置里添加一个步骤,打印环境变量值:

echo %ASPNETCORE_URLS%

修复方案:

如果该变量被设置为空或无效值,在测试代码里强制覆盖:

protected override void ConfigureWebHost(IWebHostBuilder builder)
{
    base.ConfigureWebHost(builder);
    // 强制设置环境变量,避免被CI环境覆盖
    Environment.SetEnvironmentVariable("ASPNETCORE_URLS", "http://127.0.0.1:0");
    builder.UseUrls("http://127.0.0.1:0");
    builder.ConfigureServices(ConfigureServices);
}

3. 尝试使用固定端口而非动态端口

动态端口绑定在CI环境可能遇到端口耗尽或进程隔离限制,尝试指定固定端口:

// 替换动态端口为固定端口,比如5001
builder.UseUrls("http://127.0.0.1:5001");

同时在TeamCity构建配置里,添加端口占用检查步骤,确保该端口未被其他进程占用。

4. 验证TeamCity进程的网络权限

虽然Local System账户权限很高,但TeamCity构建代理可能运行在受限的网络上下文里。可以在测试代码里添加端口绑定测试,确认网络权限:

// 在ConfigureWebHost方法中添加测试代码
using var testSocket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
try
{
    testSocket.Bind(new IPEndPoint(IPAddress.Loopback, 0));
    Console.WriteLine("Successfully bound to dynamic port.");
}
catch (SocketException ex)
{
    throw new InvalidOperationException("Failed to bind to test port, check network permissions.", ex);
}

如果抛出异常,说明TeamCity代理的网络权限存在问题,需要调整代理配置。

5. 调整TestHost的启动顺序

原代码中先启动TestHost再返回,可能在CI环境里延迟Kestrel的初始化。尝试调整启动顺序,确保Kestrel完全启动后再启动TestHost:

protected override IHost CreateHost(IHostBuilder builder)
{
    // 先配置并启动Kestrel服务器
    builder.ConfigureWebHost(webHostBuilder => webHostBuilder.UseKestrel());
    _host = builder.Build();
    _host.Start();

    // 确认获取到绑定地址
    var server = _host.Services.GetRequiredService<IServer>();
    var addresses = server.Features.Get<IServerAddressesFeature>();
    if (addresses?.Addresses?.Any() != true)
        throw new InvalidOperationException("Kestrel failed to bind address.");
    
    ClientOptions.BaseAddress = addresses.Addresses
        .Select(x => new Uri(x))
        .Last();

    // 再创建并启动TestHost
    var testHostBuilder = new HostBuilder();
    // 复制原builder的配置到testHostBuilder
    builder.ConfigureHostConfiguration(config => testHostBuilder.ConfigureHostConfiguration(config));
    builder.ConfigureAppConfiguration(config => testHostBuilder.ConfigureAppConfiguration(config));
    builder.ConfigureServices(services => testHostBuilder.ConfigureServices(services));
    var testHost = testHostBuilder.Build();
    testHost.Start();

    return testHost;
}

建议先从添加等待逻辑和检查环境变量开始排查,这两个是CI环境中最常见的问题点。

内容的提问来源于stack exchange,提问作者Dawid Sibiński

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.04.28 23:02:28