第 10 篇的单元测试用 mock 替掉数据库,快但测不到真实 SQL、索引、事务。集成测试起真依赖,验证整条链路。以前要本地装 MySQL,现在用 testcontainers 在测试里拉容器,CI 上自动跑。
1. 为什么还要集成测试
mock 假设你的 SQL 是对的,但真实 MySQL 会咬类型、咬字段长度、咬外键。事务回滚、连接池行为也得真库才测得出。集成测试补这块盲区。
2. testcontainers 起 MySQL
测试里拉一个临时 MySQL 容器,用完自动销毁:
import "github.com/testcontainers/testcontainers-go" func startMySQL(t *testing.T) string { ctx := context.Background() req := testcontainers.ContainerRequest{ Image: "mysql:8.0", ExposedPorts: []string{"3306/tcp"}, Env: map[string]string{"MYSQL_ROOT_PASSWORD": "pass", "MYSQL_DATABASE": "test"}, WaitingFor: wait.ForListeningPort("3306/tcp"), } c, err := testcontainers.GenericContainer(ctx, testcontainers.GenericContainerRequest{ ContainerRequest: req, Started: true, }) require.NoError(t, err) t.Cleanup(func() { c.Terminate(ctx) }) host, _ := c.Host(ctx) port, _ := c.MappedPort(ctx, "3306") return fmt.Sprintf("root:pass@tcp(%s:%s)/test?parseTime=true", host, port.Port()) }
3. 测试里跑迁移再测
func TestCreateOrder_Integration(t *testing.T) { dsn := startMySQL(t) db, _ := gorm.Open(mysql.Open(dsn)) db.AutoMigrate(&Order{}, &User{}) repo := NewOrderRepo(db) svc := NewOrderSvc(repo) h := &OrderHandler{svc: svc} r := gin.New() r.POST("/orders", h.Create) w := httptest.NewRecorder() req, _ := http.NewRequest("POST", "/orders", strings.NewReader(`{"uid":1,"amount":100}`)) req.Header.Set("Content-Type", "application/json") r.ServeHTTP(w, req) assert.Equal(t, 201, w.Code) var n int64 db.Model(&Order{}).Count(&n) assert.Equal(t, int64(1), n) }
真实落库,最后数行数确认写入,比 mock 可信。
4. 接 Redis 同理
换 Image: "redis:7",拿到地址后初始化 rdb,测缓存命中、失效那套逻辑。多个容器用同一个 ctx 管理生命周期。
5. GitHub Actions 跑 CI
name: test on: [push] jobs: integration: runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 - uses: actions/setup-go@v5 with: { go-version: "1.27" } - run: go test -tags=integration ./... -cover
testcontainers 在 CI 里用 runner 自带的 Docker,不用额外装。给集成测试打 integration build tag,本地 go test 默认不拉容器,跑得快;CI 才带 tag 跑全套。
6. 上线清单
集成测试只在 CI 或显式 tag 下跑,别拖慢本地单测。容器用完 t.Cleanup 销毁,不占资源。迁移用同一份代码,保证测的是线上结构。敏感配置走 CI secret,不进仓库。覆盖率报告上传,低于阈值卡合并。
下一篇讲指标监控,把线上运行状态变成能看的曲线。