Gemini3.5自动写单元测试覆盖率提升实操复盘
最近在leadhi.cn 这个AI模型聚合平台上切换了几个模型专门跑Go单元测试生成,发现不同模型对Go语言特性的理解差距还挺大。这篇文章是我用Gemini 3.5 Flash实际跑项目的复盘,踩了不少坑,也有真实收获。
概要
2026年6月,AI辅助编码已经不是新鲜事,但"AI写的测试能不能直接用"这个问题,大多数开发者心里还是没底。
我手上有一个Go微服务项目,测试覆盖率长期卡在47%左右。手动补测试太痛苦,正好Gemini 3.5 Flash刚发布不久,Terminal-Bench编程能力得分76.2%,直接越级超过了上一代3.1 Pro的70.3%。拿它跑了一轮,覆盖率拉到83%。
本文记录整个实操过程,包含prompt设计、踩坑修正、模型对比,以及一个核心判断:AI生成单元测试已经能用,但不能盲信。
整体架构流程
我的实操流程分四步,每一步都有明确的输入输出:
第一步:项目结构扫描
把项目目录和函数签名喂给模型。Gemini 3.5 Flash的上下文窗口有100万token,一个中等Go项目全量塞进去没问题。
第二步:分层生成测试
Go项目典型三层:handler → service → repository。每层单独给prompt,附上源码。一次丢太多,模型会把不同层的依赖关系搞混。
第三步:运行 + 覆盖率分析
Go 1.2起就内置了覆盖率工具,原理是重写源码添加插桩,编译运行后统计数据。命令很简单:
bash
bash
go test ./... -coverprofile=coverage.out go tool cover -html=coverage.out
看HTML报告,把未覆盖的函数提取出来,再喂给模型补测。
第四步:人工审查
这一步不能跳。AI生成的测试有两类系统性问题,后面详细说。
技术名词解释
Gemini 3.5 Flash:Google发布的轻量级多模态模型,原生支持文本、图像、音频、视频输入,100万token上下文,输出速度约290 tokens/s。定价:输入1.50/百万token,输出1.50/百万token,输出9.00/百万token。
单元测试覆盖率:衡量测试执行了多少源代码的比例。Go用go test -cover输出百分比。行业一般要求50%-60%,资金型服务要求80%。
errors.Is / errors.As:Go 1.13引入的错误链判断工具。Is比较错误值是否相等,As做错误类型断言。这两个函数是Go错误处理的核心,也是AI最容易写错的地方。
Mock打桩:用函数A替换函数B,屏蔽外部依赖(数据库、文件等),保证测试稳定性和幂等性。Go中常用monkey库实现运行时函数替换。
Benchmark基准测试:Go内置的性能测试框架,以Benchmark开头,入参testing.B,通过b.N反复递增循环测试代码性能。
技术细节
1. Prompt设计:分层喂代码效果最好
一次把整个项目丢给模型,效果很差。我试过三次,生成的测试要么遗漏关键分支,要么把不同层的逻辑混在一起。
后来改成按层单独喂,prompt模板:
text
text
你是Go测试工程师。以下是[handler/service/repository]层代码: [粘贴代码] 请用标准库testing生成单元测试,覆盖:正常路径、边界条件、错误返回。 不使用第三方测试框架。
这样命中率最高。测试文件以_test.go结尾,函数以Test开头,模型对这个命名规范倒是从来没有搞错过。
2. errors.Is和errors.As:模型会搞混
这是踩的最大的坑。
Go没有try-catch,错误靠返回值传递。Go 1.13之后用fmt.Errorf的%w包装错误,配合errors.Is和errors.As判断错误链。
Gemini 3.5生成包装代码没问题:
go
go
err2 := fmt.Errorf("外层: %w", err1)
但判断逻辑上,它偶尔把Is和As用混:
go
errors.Is(err, &AppError{}) // 这是错的
errors.Is是值比较,errors.As才是类型断言。正确写法:
go
var target *AppError errors.As(err, &target) // 类型匹配
而且As的第二个参数类型要和目标类型一致。如果函数返回的是FuncErr实例,target就声明为FuncErr;如果返回的是&FuncErr,target就声明为*FuncErr。这个细节模型偶尔会忽略。
3. Mock和外部依赖:模型生成的mock基本能用
真实项目中测试需要数据库、文件等外部依赖。单元测试要求稳定性和幂等性——能在任何环境运行,每次结果一致。
Gemini 3.5生成的mock代码风格偏保守,基本是用monkey库的Patch方法替换原函数:
go
monkey.Patch(ReadFileLine, func() string { return "mock_data" }) defer monkey.Unpatch(ReadFileLine)
原理是在运行时通过unsafe包替换内存中的函数地址。模型生成这类模式化代码质量稳定。
4. 覆盖率提升:分支覆盖是关键
Go的覆盖率工具以"基本块"为单位标注,通常由大括号界定。一个函数只有if-else两个分支,只测一边覆盖率就只有50%。
我的做法:把覆盖率报告里未覆盖的行号提取出来,针对性地喂给模型补测用例。比如一个判断是否及格的函数,模型默认只生成"输入70"的测试,覆盖率66.7%。加一个不及格的case,就能拉到100%。
循环跑三轮,47% → 71% → 79% → 83%。
5. 模型对比:Flash快但偶尔自信地错
同一批代码我也跑了Claude。结论:
| 维度 | Gemini 3.5 Flash | Claude |
|---|---|---|
| 生成速度 | ~290 tokens/s | 约80-90 tokens/s |
| 一次可用率 | 约70% | 约80% |
| 覆盖率提升速度 | 快,用例多 | 慢,但质量高 |
| 审查成本 | 较高 | 较低 |
Flash赢在速度和广度,Claude赢在精度。Flash有时会"自信地写错"——代码看着没问题,跑起来有隐蔽bug。
小结
实操结论:Gemini 3.5 Flash用于Go单元测试生成,覆盖率从47%拉到83%是可以实现的。关键在于分层喂prompt、循环补测、人工审查三步走。errors.Is/errors.As的混淆和recover调用位置是两个高频错误点,审查时重点盯。
趋势判断:Anthropic 2026编程趋势报告指出,AI正从"辅助写代码"进化到"Agent接管开发流程",任务跨度从分钟级扩展到周级。单元测试生成是这个趋势中最容易落地的场景——输入输出明确,结果可量化验证。
一个务实的建议:不同模型各有擅长,日常CRUD和单测用Flash足够,复杂重构和架构设计还是上Sonnet。多模型切换跑,比押注单个模型更稳。
AI是测试实习生,不是测试主管。审查这一步,省不了。
更多推荐


所有评论(0)