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

.NET 6 + xUnit使用Test Containers集成测试卡住求助

问题:.NET 6 API xUnit集成测试卡在第一步(Test Containers容器已正常启动)

正在为.NET 6 API项目使用Test Containers运行xUnit集成测试,容器实例已正常启动,且能在浏览器中访问目标端点并获取响应,但xUnit测试始终卡在第一个测试步骤,无任何执行动作,相关截图如下:

测试卡住截图1
测试卡住截图2

测试代码

public class IntegrationTestWebAppFactory : WebApplicationFactory<Program>, IAsyncLifetime
{
    private readonly IContainer _apiContainer;
    public HttpClient Client { get; private set; }

    public IntegrationTestWebAppFactory()
    {
        Console.WriteLine("initiating the factory");
        _apiContainer = new ContainerBuilder()
                                  .WithImage("***.azurecr.io/weather.api:v1")
                                   .WithEnvironment("DOCKER_REGISTRY_URL", "***")
                                   .WithEnvironment("DOCKER_REGISTRY_USERNAME", "***")
                                   .WithEnvironment("DOCKER_REGISTRY_PASSWORD", "***")
                                   .WithPortBinding(8080, 80)
                                    .WithWaitStrategy(Wait.ForUnixContainer().UntilHttpRequestIsSucceeded(r => r.ForPort(8080)))
                                   .Build();
    }

    public async Task InitializeAsync()
    {
        Console.WriteLine("InitializeAsync");
        await _apiContainer.StartAsync().ConfigureAwait(false);

        Client = new HttpClient
        {
            BaseAddress = new Uri("http://localhost:8080/")
        };
        Console.WriteLine("InitializeAsync done");
    }

    async Task IAsyncLifetime.DisposeAsync()
    {
        Console.WriteLine("DisposeAsync");
        await _apiContainer.DisposeAsync().ConfigureAwait(false);
    }
}


public class UnitTest1 : IClassFixture<IntegrationTestWebAppFactory>
{
    private readonly IntegrationTestWebAppFactory _factory;
    public UnitTest1(IntegrationTestWebAppFactory factory)
    {
        _factory = factory;
    }

    [Fact]
    public async Task Should_Return_Forcast()
    {
        //   Arrange
        if(_factory is null)
        {
            Console.WriteLine("factory is null");
            return;
        }
        var client = _factory.Client;
        //   Act
        var response = await client.GetAsync("http://localhost:8080/WeatherForecast");
        response.EnsureSuccessStatusCode();
        var responseString = await response.Content.ReadAsStringAsync();
        Assert.NotEmpty(responseString);
    }
}

问题排查与解决方案

1. 移除WebApplicationFactory的不必要继承

你同时继承了WebApplicationFactory<Program>并手动启动Test Containers容器,WebApplicationFactory本身会自动启动一个测试用Web实例,两者的初始化逻辑可能导致线程阻塞。如果你的目标是测试已构建的镜像,不需要继承WebApplicationFactory,直接实现IAsyncLifetime即可。

2. 优化等待策略

当前的等待策略仅检查端口连通性,建议明确指定测试端点路径并增加超时时间,确保容器内服务完全就绪:

.WithWaitStrategy(Wait.ForUnixContainer()
    .UntilHttpRequestIsSucceeded(r => r.ForPort(8080).ForPath("/WeatherForecast"))
    .WithTimeout(TimeSpan.FromMinutes(2)))

3. 调整异步上下文处理

移除InitializeAsync中.ConfigureAwait(false),避免线程上下文切换导致的初始化阻塞:

await _apiContainer.StartAsync();

4. 简化HttpClient请求地址

已设置BaseAddress的情况下,请求时使用相对路径即可,避免冗余的完整地址解析问题:

var response = await client.GetAsync("/WeatherForecast");

修改后的示例代码

public class IntegrationTestFixture : IAsyncLifetime
{
    private readonly IContainer _apiContainer;
    public HttpClient Client { get; private set; }

    public IntegrationTestFixture()
    {
        Console.WriteLine("initiating the fixture");
        _apiContainer = new ContainerBuilder()
                              .WithImage("***.azurecr.io/weather.api:v1")
                              .WithEnvironment("DOCKER_REGISTRY_URL", "***")
                              .WithEnvironment("DOCKER_REGISTRY_USERNAME", "***")
                              .WithEnvironment("DOCKER_REGISTRY_PASSWORD", "***")
                              .WithPortBinding(8080, 80)
                              .WithWaitStrategy(Wait.ForUnixContainer()
                                  .UntilHttpRequestIsSucceeded(r => r.ForPort(8080).ForPath("/WeatherForecast"))
                                  .WithTimeout(TimeSpan.FromMinutes(2)))
                              .Build();
    }

    public async Task InitializeAsync()
    {
        Console.WriteLine("InitializeAsync");
        await _apiContainer.StartAsync();

        Client = new HttpClient
        {
            BaseAddress = new Uri("http://localhost:8080/")
        };
        Console.WriteLine("InitializeAsync done");
    }

    public async Task DisposeAsync()
    {
        Console.WriteLine("DisposeAsync");
        await _apiContainer.DisposeAsync();
    }
}

public class UnitTest1 : IClassFixture<IntegrationTestFixture>
{
    private readonly IntegrationTestFixture _fixture;
    public UnitTest1(IntegrationTestFixture fixture)
    {
        _fixture = fixture;
    }

    [Fact]
    public async Task Should_Return_Forcast()
    {
        // Arrange
        if (_fixture is null)
        {
            Console.WriteLine("fixture is null");
            return;
        }
        var client = _fixture.Client;

        // Act
        var response = await client.GetAsync("/WeatherForecast");
        response.EnsureSuccessStatusCode();
        var responseString = await response.Content.ReadAsStringAsync();

        // Assert
        Assert.NotEmpty(responseString);
    }
}

内容的提问来源于stack exchange,提问作者Reza Shirazi

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.26 08:30:57