测试必须隔离状态、避免共享可变资源,每个测试独占实例;TestMain仅限只读初始化;testdata路径需动态构建且文件语义化;辅助函数须调t.Helper()并返回error。
测试数据必须隔离,不能共享可变状态
Go测试默认并发执行(go test 默认启用 -p 并行),一旦多个测试共用一个全局变量、数据库连接或内存缓存,就极易出现“前一个测试改了数据,后一个测试读到脏值”的问题。这不是偶发,是必然污染。
- 避免在
init()或包级变量中初始化可变资源(如var db *sql.DB) - 每个测试应独占自己的数据库实例(如用
sqlite.Memory)、独立的 mock 服务端口、或通过tempfile创建临时目录 - 若必须复用资源(如 HTTP server),确保其 handler 是无状态的,且每次请求都重置内部状态(比如用
sync.Map按t.Name()隔离数据)
TestMain 中只做一次性只读初始化
TestMain 很容易被当成“万能初始化入口”,但滥用它会让测试失去可重复性——比如在里面启动一个全局 HTTP server,却没保证每次测试前清空它的路由表或中间件栈,后续测试就可能因路由冲突或中间件副作用而失败。
- 适合放:加载配置文件(
json.Unmarshal到只读结构体)、构建不可变的 fixture 数据(如ValidUser常量)、预编译正则表达式 - 禁止放:打开可写数据库连接、注册全局 hook、修改
os.Environ()、启动监听端口(除非你明确控制端口分配并确保每次 clean) - 务必在
m.Run()后执行teardown,哪怕只是os.RemoveAll(tempDir),否则下次运行可能因残留文件失败
testdata 目录要配合 filepath.Join 动态读取
硬编码路径(如 "./testdata/user.json")会导致测试在不同工作目录下失败;而直接把 JSON 内容写进测试函数里,又让数据无法复用、难维护、易过期。