离线环境自测需剥离网络、服务、时间源依赖:HTTP mock必须用httptest.Server而非httpmock,避免遗漏Transport等真实行为;时间敏感逻辑须抽象为clock.Clock接口,禁用硬编码time.Now()。

离线环境自测不是“把代码拷过去跑一遍”就行,关键在于提前剥离对外部网络、服务、时间源的隐式依赖——否则go test一运行就卡在 DNS 解析、HTTP 超时或 time.Now() 的不可控返回上。
mock 外部 HTTP 服务必须用 httptest.Server,别碰 httpmock
离线时最常见的是测试里调用了第三方 API,比如调阿里云 KMS 签名、请求 GitHub Webhook 验证。直接 mock http.DefaultClient 或用第三方库(如 httpmock)容易漏掉 Transport 层配置、重试逻辑、TLS 握手失败等真实行为。
-
httptest.Server启一个真实监听的本地服务,端口由系统自动分配,完全绕过 DNS 和网络栈 - 它返回的是真实
*http.Server实例,可复用你生产代码中所有 Transport/Timeout/Proxy 设置 - 注意:启动后要显式调用
server.Close(),否则 goroutine 泄漏,go test -race会报错 - 示例片段:
srv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { w.WriteHeader(200) w.Write([]byte(`{"key":"val"}`)) })) defer srv.Close() // 必须加 client := &http.Client{Transport: &http.Transport{}} resp, _ := client.Get(srv.URL + "/api")
时间敏感逻辑要用 clock.Clock 接口抽象
凡涉及定时器、超时判断、缓存过期、JWT 签发时间验证的代码,硬写 time.Now() 或 time.AfterFunc() 就等于放弃离线测试能力。
- 定义接口:
type Clock interface { Now() time.Time; After(d time.Duration) - 生产代码中把
time.Now替换为注入的c.Clock.Now(),构造函数接收Clock参数 - 测试时传入
clock.NewMockClock()(可用 github.com/benbjohnson/clock)或自己实现一个返回固定时间的 struct - 特别注意:
time.Sleep()在离线测试里不能靠“等”,必须改用clock.Sleep()或 channel 同步
依赖数据库?用 sqlite 内存模式 + migrate 工具预置 schema
离线没法连 MySQL 或 PostgreSQL,但又不能删掉 DAO 层测试——这时候 sqlite 的 :memory: 模式是唯一轻量且语义兼容的方案。
立即学习“go语言免费学习笔记(深入)”;
- 连接字符串用
"sqlite://:memory:?_fk=1",确保外键约束生效 - 用
migrate(github.com/golang-migrate/migrate)或goose加载 SQL 迁移文件,避免手写CREATE TABLE - 每个 test 函数单独开新内存 DB,不共享连接,避免事务污染
- 注意:SQLite 不支持
JSON类型原生操作,如果业务用了 PostgreSQL 的jsonb字段,测试时得降级为TEXT并手动json.Marshal/Unmarshal
go mod vendor 后,test 仍报 missing package?检查 replace 和 indirect
离线环境 go test 报 cannot find module,大概率不是 vendor 没做全,而是 go.mod 里有 replace 指向本地路径,或某些 indirect 依赖没被 vendor 收录。
- 执行
go mod vendor -v,看输出末尾是否出现skipping vendor for replaced module - 若有
replace,离线前必须先go mod edit -dropreplace ./local/path,再go mod tidy && go mod vendor - 检查
vendor/modules.txt是否包含所有indirect行;若缺失,手动运行go list -m all | grep indirect,确认这些模块是否真被引用 - 终极手段:
go test -mod=vendor强制只读 vendor 目录,跳过任何远程解析
离线自测最难缠的从来不是技术点,而是那些没写进文档的隐式依赖:日志上报用的 UDP 地址、健康检查连的 consul agent、甚至测试里一行 os.Hostname() 在容器里返回空字符串——这些都得在第一次离线 run test 前,用 grep -r "http\|net\|time\|os\.Hostname\|os\.User\|exec\.Command" ./... 扫一遍,逐个围剿。


















