testcontainers-go 需用 go get github.com/testcontainers/testcontainers-go 安装;PostgreSQL 连接须用 container.Host(ctx) 和 container.MappedPort(ctx, "5432/tcp") 获取地址与端口,加 sslmode=disable;等待就绪优先用 wait.ForListeningPort("5432/tcp");Terminate 应在 TestMain 中统一管理,避免残留。

testcontainers-go 不是官方库,别装错版本——直接 go get github.com/testcontainers/testcontainers-go 就行,Java/Python 的客户端连不上你的 Docker daemon。
容器启动后连不上 PostgreSQL?别硬写 localhost:5432
PostgreSQL 容器的 5432 端口不会自动映射到宿主机的 5432;容器内 localhost 和宿主机 localhost 是两个网络空间。硬写 postgres://user:pass@localhost:5432/db 必然失败。
- 必须用
container.Host(ctx)拿宿主机可访问 IP(Linux 是localhost,Mac/Windows 是 Docker 虚拟网关 IP) - 必须用
container.MappedPort(ctx, "5432/tcp")拿动态分配的真实端口,比如32768 - 连接字符串末尾一定要加
sslmode=disable,否则sql.Open会卡住或报SSL is not enabled on the server - 别在
GenericContainer返回后立刻 dial——PostgreSQL 进程可能还没 ready,得靠WaitingFor
WaitingFor 用 ForLog 还是 ForListeningPort?
对 PostgreSQL,优先用 wait.ForListeningPort("5432/tcp"),不是 ForLog。
-
ForLog("database system is ready to accept connections")看似合理,但日志输出时机不稳定:镜像版本升级、启动参数变化都可能导致日志行缺失或延迟 -
ForListeningPort是 TCP 层探测,更底层、更可靠;配合WithStartupTimeout(60 * time.Second)和WithPollInterval(2 * time.Second)可控性更强 - 如果非要用 ForLog,记得加
WithOccurrence(1)或WithStartupTimeout,否则默认只等第一次出现,而 PostgreSQL 有时会重复打印就绪日志
测试结束容器没销毁?Terminate() 顺序和 defer 位置很关键
defer container.Terminate(ctx) 写在测试函数里看似稳妥,但 panic 时可能不执行;写在子函数里更危险——Go 的 defer 只绑定当前函数作用域。
立即学习“go语言免费学习笔记(深入)”;
- 最稳做法:在
TestMain中统一管理,os.Exit(m.Run())前遍历所有已启容器调.Terminate(ctx) - 别先调
.Stop(ctx)再.Terminate(ctx)——Terminate()默认含 Stop + Remove,Stop 后再 Terminate 实际什么也没做 - CI 环境建议加兜底清理:
docker system prune -f --filter "until=30m",防止 panic 导致容器残留 - 本地开发时开
testcontainers.WithLogger(log.New(os.Stderr, "", 0)),能看到容器启停和日志,排查残留一目了然
ExposedPorts 字段看着可选,其实漏掉就彻底失效;WaitingFor 不设超时,测试可能挂死十分钟;Terminate 放错位置,下次运行直接端口冲突——这些都不是边缘 case,而是每轮调试必踩的点。


















