Go测试文件必须与源码同包(非仅同目录),即utils目录下的_test.go需声明package utils而非utils_test;Test_前缀是官方支持的稳定命名约定,用于测试未导出函数;应通过参数化依赖(如time.Now、配置等)消除隐式依赖,而非硬导出或反射调用。

测试文件必须和源码同包,不是同目录就行
报 undefined: validateToken 错误,90% 是因为测试文件写了 package utils_test,哪怕它就放在 utils/ 目录下,也进不了 utils 包作用域。Go 的可见性只认包名,不看路径。
正确做法是:把 utils_test.go 文件放在 utils/ 目录里,但第一行写 package utils(不是 utils_test),后缀仍是 _test.go。IDE 有时默认生成 xxx_test 包,得手动改。
-
go test会自动识别_test.go文件,但不会帮你修正包名 - 同包内,
validateToken和Test_validateToken都能直接调用、无需导出 - 别用
package utils_internal或package testutils—— 这等于新建隔离包,私有函数照样不可见
Test_ 开头是合法且稳定的命名约定
go test 明确支持 Test_ 命名(下划线紧接 Test 后),用于测试未导出函数。这不是临时技巧,Go 1.21+ 已官方确认,工具链和 IDE 都能识别跳转、补全、覆盖率统计。
例如,被测函数是 func parseConfig() error,对应测试函数应为:
立即学习“go语言免费学习笔记(深入)”;
func Test_parseConfig(t *testing.T) {
// 直接调用 parseConfig()
err := parseConfig()
if err != nil {
t.Fatal(err)
}
}
- ❌
test_parseConfig(小写开头):不被go test发现 - ❌
TestParseConfig(驼峰):会被当成测试导出函数ParseConfig,找不到就报错 - ✅
Test_parseConfig:匹配Test_模式,且与被测函数名严格对应
别硬导出,优先重构解耦逻辑
看到 func validateToken(token string) error 太复杂,直接改成 ValidateToken 再测?这等于把实现细节钉死在 API 上。下次加 context.Context 或换签名,所有调用方(包括测试)都得改。
Go 配置库,使用 spf13/viper — 分层优先级(flag > env >file > KV > default),提供 BindPFlag/BindPFlags、SetEnvPrefix + SetEnvKeyReplace 等功能。
真正该做的,是把纯逻辑抽出来:
- 提取导出函数:
func ValidateTokenBytes(token []byte, now time.Time, cfg Config) error - 原私有函数降级为胶水层:
func validateToken(token string) error只负责解析字符串、调用新函数、包装错误 - 对
ValidateTokenBytes写标准TestValidateTokenBytes,输入明确、无副作用、易 mock
这样既保持封装,又让核心逻辑可测、可复用、可演进。
真正卡住你的往往不是“私有”,而是隐式依赖
就算 Test_validateToken 能跑起来,也可能一到 CI 就失败——原因通常是它偷偷依赖了 os.Getenv("SECRET_KEY")、time.Now() 或全局 HTTP client。
解决办法不是绕过私有性,而是让依赖显式化:
- 把时间注入为参数:
func validateToken(token string, now func() time.Time) error - 测试时传入固定值:
now: func() time.Time { return time.Unix(1717250000, 0) } - 配置、密钥、HTTP 客户端都作为参数或接口传入,而非硬编码读取
一个没有隐式依赖的私有函数,天然适合单元测试;反之,即使你用反射强行调用,测试也脆弱、难维护、CI 不稳定。

















