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

使用Playwright与Node Testcontainers重启Postgres容器报错求助

问题

使用Playwright结合Node Testcontainers在Docker容器中运行测试,需求如下:

  • API容器仅启动一次,所有测试完成后再停止
  • 每个测试执行前重启Postgres数据库容器

当前调整配置后出现错误:(HTTP code 409) container stopped/paused failed to create Container,数据库容器似乎在测试启动前就已退出。

现有代码
let databaseContainer;
let apiContainer;
let network

test.beforeAll(async () => {
    network = await new Network().withName("test");
   
    apiContainer = await new GenericContainer('apicontainer:1.0')
        .withName('api')
        .withEnvironment({ ASPNETCORE_ENVIRONMENT: 'Development' })
        .withEnvironment({
            CONNECTIONSTRINGS_POSTGRES: 'Host=postgres;Username=admin;Password=admin;Database=Test',
        })
        .withEnvironment({
            CONNECTIONSTRINGS__POSTGRES: 'Host=postgres;Username=admin;Password=admin;Database=Test',
        })
        .withExposedPorts({ container: 80, host: 1234 }).start();
});

test.beforeEach(() => {
 databaseContainer = await new PostgreSqlContainer('postgres:14.6')
        .withName('database')
        .withNetworkMode(network.getName())
        .withEnvironment({ POSTGRES_USER: 'admin' })
        .withEnvironment({ POSTGRES_PASSWORD: 'admin' })
        .withEnvironment({ POSTGRES_DB: 'Test' })
        .withDatabase('Test')
        .withExposedPorts({ container: 5432, host: 5432 });
        .withNetworkMode(network.getName))
        .start();
})

test.afterEach(() => {
    await databaseContainer.stop();
})

test.afterAll(async () => {
    await apiContainer.stop();
})
问题分析与修复方案

核心错误点

  1. 语法错误:

    • withExposedPorts语句末尾多了分号,导致后续withNetworkMode调用报错
    • network.getName)少了括号,应为network.getName()
    • 重复调用withNetworkMode,属于冗余配置
  2. 异步逻辑缺失:beforeEach和afterEach未声明为async函数,内部的await无法生效,容器操作未完成就进入下一步,导致容器状态异常。

  3. 容器命名与端口冲突:

    • 固定使用database作为容器名,旧容器未完全销毁时,新容器创建会触发Docker的同名冲突(409错误)
    • 固定绑定主机端口5432,旧容器未释放端口时,新容器启动失败
  4. 网络关联错误:API容器未加入测试网络test,导致API无法访问Postgres容器,可能引发数据库容器因连接问题提前退出。

  5. 数据库就绪等待缺失:未等待Postgres容器完全启动就绪就开始测试,可能导致测试执行时数据库不可用,容器异常退出。

修复后的代码

let databaseContainer;
let apiContainer;
let network;

test.beforeAll(async () => {
    // 创建测试专用网络并启动
    network = await new Network().withName("test").start();
   
    // 启动API容器并加入测试网络
    apiContainer = await new GenericContainer('apicontainer:1.0')
        .withName('api')
        .withNetwork(network) // 将API容器加入测试网络
        .withEnvironment({ 
            ASPNETCORE_ENVIRONMENT: 'Development',
            CONNECTIONSTRINGS_POSTGRES: 'Host=postgres;Username=admin;Password=admin;Database=Test',
            CONNECTIONSTRINGS__POSTGRES: 'Host=postgres;Username=admin;Password=admin;Database=Test'
        })
        .withExposedPorts(80) // 不固定主机端口,由Testcontainers自动分配
        .start();
});

// 每个测试前启动新的Postgres容器
test.beforeEach(async () => {
    databaseContainer = await new PostgreSqlContainer('postgres:14.6')
        .withNetwork(network) // 加入测试网络
        .withEnvironment({ 
            POSTGRES_USER: 'admin',
            POSTGRES_PASSWORD: 'admin',
            POSTGRES_DB: 'Test'
        })
        .withDatabase('Test')
        .withExposedPorts(5432) // 自动分配主机端口
        .waitForHealthCheck() // 等待数据库健康检查通过
        .start();
});

// 每个测试后停止并移除数据库容器
test.afterEach(async () => {
    await databaseContainer.stop();
    await databaseContainer.remove(); // 确保容器被完全移除,避免命名冲突
});

test.afterAll(async () => {
    await apiContainer.stop();
    await apiContainer.remove();
    await network.remove(); // 清理测试网络
});

关键优化说明

  • 移除固定容器名和主机端口,避免冲突,由Testcontainers自动管理资源
  • 确保所有涉及异步操作的钩子函数都声明为async
  • 为API容器和数据库容器加入同一测试网络,保证服务间可访问
  • 增加waitForHealthCheck()确保Postgres完全就绪后再执行测试
  • 在afterEach和afterAll中主动移除容器和网络,彻底清理资源

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.17 21:15:23