Go测试与集成测试区别_Go测试类型对比说明

8次阅读

单元测试只验证函数逻辑,不接触数据库和网络;集成测试则需启动真实依赖(如postgresql)验证整条链路。二者应通过构建标签隔离,且集成测试重在环境稳定与关键路径覆盖。

Go测试与集成测试区别_Go测试类型对比说明

单元测试只测函数,不碰数据库和网络

go 的单元测试本质是“逻辑快照”:它只验证一个函数在给定输入下是否返回预期输出,其他一概不管。这意味着 sql.Openhttp.Get、文件读写等外部调用必须被替换或绕过。

  • 常见错误:在 TestXxx 函数里直接连真实 mysql,导致测试失败不是因为逻辑错,而是因为数据库没启、密码错、表不存在
  • 正确做法:用接口抽象依赖(如定义 DBClient 接口),测试时传入 mock 实现;或用 io.Discardhttptest.NewServer标准库工具隔离 I/O
  • 性能影响:单个单元测试通常在毫秒级完成;100 个单元测试跑完一般不超过 1 秒

集成测试要启动真实依赖,比如 docker 化的 PostgreSQL

集成测试的目标不是“函数对不对”,而是“这一整条链路通不通”——比如 HTTP handler → service → repository → PostgreSQL → 返回 jsON。它必须用真实组件,否则就不是集成。

  • 使用场景:验证迁移脚本是否建表成功、gRPC client 能否连上 server、API 是否返回符合 OpenAPI 规范的字段
  • 关键技巧:用 //go:build integration 构建标签隔离,避免 go test 默认执行它们;CI 中用 go test -tags=integration -timeout=60s 控制资源消耗
  • 容易踩的坑:本地测试连了生产数据库(尤其当 DB_URL环境变量读取却没设默认值);忘记 defer db.Close() 导致连接泄漏,后续测试卡住

go test -cover 只反映单元测试覆盖率,对集成测试基本无效

Go 的覆盖率统计基于源码行是否被执行,而集成测试中大量逻辑运行在外部服务里(比如 SQL 查询本身不经过 Go 源码),所以 go test -cover 数值会严重失真。

  • 如果你跑 go test -cover 得到 85%,那只是“你写的 Go 代码里有 85% 的行被单元测试触达”,不代表业务流程覆盖充分
  • 真正需要关注的是:核心路径有没有至少一个集成测试兜底?比如 “用户注册 → 发送邮件 → 登录成功” 这个端到端流程是否被某个 TestUserRegistrationFlow 覆盖?
  • 不要为了提升 -cover 数字而给日志打印、panic 分支写无意义的单元测试;优先保障边界条件(空输入、超长字符串、数据库唯一约束冲突)有对应集成验证

别把 TestMain 当 setup,它不解决并发测试的资源竞争

func TestMain(m *testing.M) 确实适合做全局初始化(如启动一次 Docker 容器),但它不能保证每个 TestXxx 是串行执行的——Go 测试默认并发运行,多个测试同时操作同一个数据库表就会出问题。

  • 典型现象:TestCreateUserTestDeleteUser 都用 users 表,但一个刚 INSERT,另一个已 TRUNCATE,结果随机失败
  • 解决方案只有两个:要么每个测试用独立 schema 或前缀表名(如 users_test_123);要么显式加 t.Parallel() 并禁用并发(go test -p 1),但后者会让集成测试变慢数倍
  • 更务实的做法:集成测试只保留关键 happy path,其余用单元测试 + mock 覆盖分支逻辑

集成测试最难的从来不是写断言,而是让环境稳定可重现——Docker Compose 文件版本锁死、PostgreSQL 镜像用 15.3 而非 latest、所有时间相关逻辑用 clock.Now() 接口注入,这些细节比测试语法重要得多。

text=ZqhQzanResources