本地 go test 通过不等于 CI 通过,因 CI 无 GOPATH、无缓存、依赖隔离严格且不加载 .env;需用 -mod=vendor 或 -mod=readonly、GOCACHE=off、GOPROXY=direct、go mod download 预拉依赖,并启用 -race 检测竞态。

Go test 命令怎么跑才真正覆盖 CI 场景
本地 go test 通过不等于 CI 里能过。CI 环境通常没 GOPATH、没缓存、依赖隔离严格,且默认不加载 .env 或本地配置文件。
- 用
go test -mod=vendor(如果项目有vendor/)或-mod=readonly避免 CI 中意外拉取新版本模块 - 显式设置
GOCACHE=off和GOPROXY=direct,防止缓存污染或代理不可达导致失败 - 测试前加
go mod download确保所有依赖提前就位,避免超时中断 - 用
go test -race -v ./...启用竞态检测——CI 是暴露 data race 的高频场景,别等上线才翻车
GitHub Actions 中 Go 测试 workflow 容易漏掉的关键配置
很多模板直接抄 actions/setup-go 就完事,但实际会卡在 CGO、交叉编译或私有模块上。
- 若测试含 C 依赖(如
sqlite3),必须加run: sudo apt-get update && sudo apt-get install -y build-essential - 私有模块需提前配置
GIT_SSH_COMMAND或用git config --global url."git@github.com:".insteadOf "https://github.com/" - 避免用
strategy.matrix.go同时测多个 Go 版本却不锁go.mod的go 1.x声明——低版本 Go 解析高版本 module 文件会直接报错go: cannot use path@version syntax in go.mod - 上传测试覆盖率到 codecov 需先生成
coverage.out:go test -coverprofile=coverage.out -covermode=count ./...,再用codecovaction 读取
如何让单元测试不因环境变量或外部服务假失败
CI 构建节点没有数据库、Redis、HTTP mock 服务,硬连就会超时或 panic。
- 用
if os.Getenv("CI") != ""跳过集成类测试,或统一用testing.Short():运行时加-short参数跳过耗时/依赖外部的测试 - 接口层测试一律走
httptest.Server,别调真实域名;DB 层用sqlmock或内存 SQLite(sqlite3.Open(":memory:")) - 把配置注入从
os.Getenv改成结构体字段 + 构造函数传参,测试时直接 new 一个带 mock 值的实例,不碰环境变量 - 时间敏感逻辑(如 token 过期判断)用
clock.WithMock或gock拦截 time.Now() 调用,否则 CI 时间漂移会导致随机失败
覆盖率报告和失败定位为什么总对不上行号
go tool cover 在 CI 里输出 HTML 报告常出现“未覆盖”却点不开源码,或行号偏移——根本原因是工作目录和 GOPATH 不一致导致路径解析错乱。
立即学习“go语言免费学习笔记(深入)”;
- 生成 profile 时加
-coverpkg=./...显式指定包范围,避免只覆盖 main 包而忽略 internal/ 下的逻辑 - HTML 报告生成命令必须在项目根目录执行:
go tool cover -html=coverage.out -o coverage.html,否则内部链接路径失效 - GitHub Actions 中用
actions/checkout@v4后默认在/home/runner/work/repo-name/repo-name,确保go test和go tool cover都在此路径下运行 - 如果用了
go work,CI 中要先go work use ./...再跑测试,否则coverpkg无法识别 workspace 内其他模块










