go端到端测试必须用testmain统一启停真实http服务,绑定localhost:0自动分配端口并轮询健康检查,禁用httptest.newrequest+newrecorder伪测试,客户端需独立http.client,db用临时库隔离,断言覆盖状态码、headers、body结构。

Go 语言的集成测试不是靠新框架,而是用 go test 本身 + 真实依赖 + 明确边界控制。端到端测试更进一步,必须启动完整服务进程、走真实 TCP 层、连真实数据库,不能只 mock handler 或复用 http.DefaultClient。
如何用 TestMain 启动真实服务并避免端口冲突
每个测试函数里自己调 http.ListenAndServe 会触发 address already in use,并发跑直接挂。正确做法是在 TestMain 统一启停,且绑定 "localhost:0" 让系统自动分配空闲端口。
- 启动后必须轮询健康检查(如
http.Get(srv.Addr + "/health")),不能time.Sleep(2 * time.Second)—— CI 上 Docker 冷启动可能超 1.5 秒 -
srv := httptest.NewUnstartedServer(handler)后要手动srv.Start(),别漏掉 - 清理逻辑(
srv.Close())必须写在m.Run()之后、os.Exit(code)之前;defer在os.Exit前不执行 - 若用
exec.Command("./myapp", "serve", "--port", "0")启二进制,记得从srv.URL或日志里提取实际端口,再注入客户端
为什么 httptest.NewRequest + NewRecorder 不算集成测试
它绕过了 net/http.Server 的整个请求生命周期:连接建立、TLS 握手(如果启用)、header 解析、body stream 处理、中间件链、超时控制、graceful shutdown —— 这些全被跳过,本质仍是单元测试。
- 真正集成/端到端必须用
httptest.NewServer或真实http.Server,返回可被http.Client直接访问的地址 -
httptest.NewServer返回的是真实监听的服务器,支持 HTTP/2、连接复用、代理转发等生产行为 - 若要测中间件加载顺序、配置解析、信号处理、日志注入等,必须起真实进程(
exec.Command),不能只包 handler
数据库怎么隔离才不互相污染
共用一个 test_db 是最常见错误。A 测试删用户,B 测试紧接着查就 404;或者事务未回滚,C 测试看到脏数据。
- 每个测试用独立 database name:
"test_db_" + t.Name()或"test_db_" + uuid.NewString() - PostgreSQL 推荐先连
template1,执行CREATE DATABASE ... WITH TEMPLATE template0,再建新 pool 连过去 - SQLite 内存模式必须加
?cache=shared("file::memory:?cache=shared"),否则多个连接看不到彼此事务 - 禁止在
init()或包变量里初始化 DB;所有连接池应在TestXxx函数内创建或通过setupDB(t)函数按需重建
HTTP 客户端为什么不能用 http.DefaultClient
它是全局变量,测试中改 Timeout 或换 Transport 会影响其他测试;更危险的是默认启用 HTTP/2 和连接复用,在并发测试中可能复用已关闭连接,导致 connection refused 或 i/o timeout。
- 每个测试应构造自己的 client:
&http.Client{Timeout: 5 * time.Second, Transport: &http.Transport{...}} - 若被测代码封装了
*http.Client字段,测试时传入自定义实例,别依赖全局默认值 - 拦截第三方请求用
gock时,注意它只拦截http.DefaultClient或显式传入的 client —— 被测代码里没注入,就拦不到
最容易被忽略的是环境假设:本地能跑不代表 CI 能跑。Docker daemon 是否可用、localhost 在容器内是否指向宿主、PostgreSQL 初始化是否比服务启动慢 —— 这些都得在健康检查里显式验证,不能靠“应该可以”。
golang免费学习笔记(深入):立即使用
在学习笔记中,你将探索golang的核心概念和高级技巧!











