testmain 统一启停服务,用 localhost:0 自动分配端口并轮询健康检查;mock 外部依赖用 httptest.newserver;e2e 优先选 httptest 而非浏览器工具;并行测试需隔离数据库与状态。

TestMain 里启动服务,别在每个 test 函数里重复启停
Go 的 E2E 测试不是写几个 http.Get 就完事——它必须真实跑起服务、走完整链路。如果每个 func TestXXX(t *testing.T) 都自己 http.ListenAndServe,轻则端口冲突报 address already in use,重则并发测试全挂。
- 用
TestMain统一管理生命周期:一次启动、所有测试复用、退出时统一关掉 - 绑定
"localhost:0",让系统自动分配空闲端口,避免硬编码":8080" - 启动后必须轮询就绪(比如
http.Get(srv.Addr + "/health")),不能time.Sleep(2 * time.Second)—— 本地快不代表 CI 快,尤其 Docker 环境下冷启动可能超 1.5 秒 - 记得最后调
os.Exit(code),否则go test可能卡住不返回
mock 外部依赖,别调真实 Stripe / Slack / 支付宝接口
线上第三方 API 响应慢、限流、结构突变、甚至维护下线,都会让 E2E 测试随机失败。这不是你代码的问题,是测试设计错了。
- 用
httptest.NewServer替代真实下游:它返回的是真实*http.Server,http.Client能直接访问,无感知替换 - 把 mock 地址注入你的服务(比如通过构造函数参数
NewApp(WithPaymentAPI(mock.URL))或环境变量) - 别试图在
httptest.NewServer里读请求 body 做动态响应——它只支持预设返回;需要逻辑分支,得自己写个简易 handler 判断 path 或 query
用 httptest.Server 而不是 selenium/rod,除非真要测 UI 渲染
90% 的 Go 后端 E2E 测试,根本不需要打开浏览器。只要验证 HTTP 接口行为、状态码、JSON 结构、上下游数据流转是否正确,httptest.Server 就够了,且稳定、快、无依赖。
-
httptest.NewServer是标准库,零额外安装,CI 里开箱即用 - 如果非要用 rod 或 selenium(比如测登录页跳转、OAuth 回调、JS 表单提交),注意:
Page.MustNavigate后不能立刻Page.MustElement,得加page.WaitLoad()或page.WaitStable() - rod 的
MustElement是“查不到就 panic”,不是“等不到就重试”;真正可靠的是page.Element(...).WaitE(rod.EvalOnStart)
并行测试时,数据库和状态必须隔离
E2E 测试一旦涉及写数据库,t.Parallel() 就会互相污染——A 测试删了用户,B 测试紧接着查就 404;或者 A 插入 ID=1,B 也插 ID=1 报唯一键冲突。
- 每个测试用独立 schema 或 database name(如
"test_db_" + t.Name()),跑完defer dropDB(...) - 避免全局共享的内存 map / cache 实例;测试间状态要 clean,推荐用
setupDB(t)函数每次重建连接+清空表 - 如果用 Testcontainers 启 PostgreSQL,记得给每个测试分配不同容器名或端口,否则
docker run冲突
E2E 最容易被忽略的点,其实是“等待就绪”的判断逻辑——它既不能太激进(没起来就发请求,连不上),也不能太保守(sleep 过长拖慢整个测试套件)。真正的就绪,是服务已 accept 连接、路由已注册、健康检查返回 200,三者缺一不可。
golang免费学习笔记(深入):立即使用
在学习笔记中,你将探索golang的核心概念和高级技巧!











