Go集成测试应使用go test本身,通过命名规范、构建约束和环境变量控制执行,启动真实依赖并确保环境隔离与可靠清理。

Go 语言的集成测试不是靠新框架,而是靠 go test 本身 + 合理的测试组织 + 真实依赖启动 —— 关键在于区分“单元”和“集成”的边界,而不是换工具。
怎么写一个真正走通外部依赖的集成测试
集成测试要验证模块与真实依赖(如数据库、HTTP 服务、消息队列)的协作是否正确。不能 mock 掉所有外部调用,否则就退化成单元测试。
实操建议:
- 把集成测试文件命名为
xxx_integration_test.go,并用//go:build integration构建约束标记(Go 1.17+),避免日常go test误执行 - 使用环境变量控制是否启用集成测试,例如:
if os.Getenv("INTEGRATION") == "" { t.Skip("skipping integration test") } - 在
TestMain中统一启停依赖:比如用testcontainers-go启一个临时 PostgreSQL 容器,或用gomock+ 真实端口起一个 stub HTTP server - 数据库测试务必用独立 database name(如
test_db_随机字符串),避免多个测试并发污染
为什么 go test -tags=integration 会失败或跳过
常见现象是测试函数没被发现、t.Skip 被无条件触发、或构建报 build constraint excludes all Go files。
立即学习“go语言免费学习笔记(深入)”;
原因和解法:
- 构建标签写错:必须是
//go:build integration(Go 1.17+),不是// +build integration(旧语法已弃用);两者不能共存 - 文件里没写
package xxx_test—— 集成测试文件也必须遵守 Go 测试包规则 - 运行时没传
-tags=integration,或用了-tags=覆盖了默认标签(比如 CI 脚本里写了-tags=ci却漏掉integration) - 依赖未安装:比如用了
testcontainers-go但本地没装 Docker,t.Fatal前没检查docker info就直接启动容器,导致 panic 而非 skip
如何让 HTTP 集成测试不依赖线上服务
不要在集成测试里直连 https://api.example.com —— 不稳定、慢、不可控、可能触发限流。
替代方案:
- 用
httptest.NewUnstartedServer起一个未监听的 server,手动绑定到随机端口(srv.Start),测试完调srv.Close() - 对第三方 API,用
gock拦截 HTTP client 请求(注意:它只拦截http.DefaultClient或显式传入的 client,别忘了在被测代码里注入) - 如果被测逻辑封装了
*http.Client字段,测试时传入自定义&http.Client{Transport: &http.Transport{...}},把 Transport 替换为gock.Transport - 避免在测试中用
http.Get这类全局函数 —— 它绕过所有可替换机制,导致无法拦截
数据库集成测试的事务陷阱
很多人想用事务 rollback 实现测试隔离,但在 PostgreSQL/MySQL 中,长事务会影响 MVCC 行为;更严重的是,某些操作(如 DDL、TRUNCATE、序列重置)无法回滚。
更可靠的做法:
- 每个测试用唯一 schema 或 database name(如
testdb_12345),测试结束用DROP DATABASE彻底清理 - 用
pgxpool或sqlx的Exec在测试前执行CREATE SCHEMA IF NOT EXISTS test_schema,再设search_path - 若必须复用 DB,改用
TRUNCATE TABLE x, y, z RESTART IDENTITY CASCADE(PostgreSQL),比事务更干净 - 注意连接池复用:测试间别共享同一个
*sql.DB实例,否则事务状态、prepared statement 缓存可能互相干扰
集成测试最难的不是写断言,而是让环境可重现、清理可信赖、失败可调试 —— 很多时候你得在日志里加 t.Log("DB connection string:", connStr),而不是假设它和本地配置一致。


















