
摘要 你是否希望:
关键在于:让机器理解代码中的业务语义。
本文手把手教你用 Go 官方 go/ast 包,从真实 Go 函数中自动提取:
✅ 条件分支(if/else)
✅ 业务规则(如 “VIP 用户免运费”)
✅ 返回值逻辑
✅ 关键依赖调用
所有代码 100% 本地运行,无需外部服务,输出结构化 JSON,可直接喂给 LLM 生成测试用例。
很多团队尝试用 AI 生成测试,但效果不佳,原因往往是:
**AI 只看到代码文本,看不懂“业务意图”**。
例如这段 Go 代码:
go编辑
func NeedCaptcha(phone string) bool {
if phone == "" {
return true
}
member, _ := memberRepo.GetByPhone(phone)
if member != nil && member.IsVIP {
return false // VIP 免验证码
}
return true
}
人类一眼看出三条规则:
但普通 LLM 可能只生成:“测试空字符串、非空字符串”——漏掉 VIP 这个关键业务概念。
而 AST 解析 能精准捕获:
if 条件中的 member.IsVIPreturn false 的上下文memberRepo.GetByPhone 的依赖💡 **AST = 代码的“骨骼”**,比正则或字符串匹配更可靠。
Go 标准库 go/ast 和 go/parser 可将源码转为树形结构。
关键节点类型:
表格
函数声明我们只需遍历这棵树,提取关键信息。
user_service.go)go编辑
package service
import "your-project/repo"
type UserService struct {
memberRepo repo.MemberRepo
}
// NeedCaptcha 判断是否需要图形验证码
// 业务规则:
// - 手机号为空:需要
// - 已注册 VIP 会员:不需要
// - 其他:需要
func (s *UserService) NeedCaptcha(phone string) bool {
if phone == "" {
return true
}
member, err := s.memberRepo.GetByPhone(phone)
if err != nil {
return true // 查询失败视为新用户
}
if member != nil && member.IsVIP {
return false
}
return true
}
extract_rules.go)go编辑
func main() {
// 输出结构化规则
fmt.Printf("Extracted rules for %s:\n", rules.Name)
for i, r := range rules.Rules {
fmt.Printf("%d. IF %s → %s\n", i+1, r.Condition, r.Action)
if len(r.Calls) > 0 {
fmt.Printf(" Calls: %v\n", r.Calls)
}
}
}
func extractRulesFromFunc(fn *ast.FuncDecl) []Rule {
// 遍历函数体
ast.Inspect(fn.Body, func(n ast.Node) bool {
switch x := n.(type) {
case *ast.CallExpr:
// 记录函数调用,如 s.memberRepo.GetByPhone
if sel, ok := x.Fun.(*ast.SelectorExpr); ok {
callStr := fmt.Sprintf("%s.%s", exprToString(sel.X), sel.Sel.Name)
currentCalls = append(currentCalls, callStr)
}
case *ast.IfStmt:
// 检查 Then 分支是否有 return
if ret, ok := hasReturn(x.Body); ok {
action = ret
}
return true
})
return rules
}
bash编辑
go run extract_rules.go
输出结果:
文本编辑
Extracted rules for NeedCaptcha:
1. IF phone == "" → true
2. IF err != nil → true
Calls: [s.memberRepo.GetByPhone]
3. IF member != nil && member.IsVIP → false
Calls: [s.memberRepo.GetByPhone]
4. IF default → true
Calls: [s.memberRepo.GetByPhone]
✅ 完美捕获所有业务分支!
✅ 识别出关键字段 member.IsVIP
✅ 记录依赖调用 s.memberRepo.GetByPhone
将上述 JSON 结构喂给本地 LLM(如 Ollama Qwen):
json编辑
{
"name": "NeedCaptcha",
"rules": [
{"condition": "phone == \"\"", "action": "true"},
{"condition": "err != nil", "action": "true", "calls": ["s.memberRepo.GetByPhone"]},
{"condition": "member != nil && member.IsVIP", "action": "false", "calls": ["s.memberRepo.GetByPhone"]},
{"condition": "default", "action": "true"}
]
}
Prompt 示例:
“你是一名 QA,请为 NeedCaptcha 函数生成 Go 单元测试,覆盖所有规则。使用 testify/mock mock memberRepo。”
LLM 将输出:
go编辑
func TestNeedCaptcha(t *testing.T) {
// Case 1: empty phone
assert.True(t, svc.NeedCaptcha(""))
// Case 2: repo error
mockRepo.On("GetByPhone", "138...").Return(nil, errors.New("db error"))
assert.True(t, svc.NeedCaptcha("138..."))
// Case 3: VIP member
mockRepo.On("GetByPhone", "139...").Return(&Member{IsVIP: true}, nil)
assert.False(t, svc.NeedCaptcha("139..."))
// Case 4: normal user
mockRepo.On("GetByPhone", "137...").Return(&Member{IsVIP: false}, nil)
assert.True(t, svc.NeedCaptcha("137..."))
}
✅ 业务规则 → 测试用例,全自动!
// @rule VIP 用户免验证码,AST 可一并提取。switch、for、错误处理的解析。不要让 AI 猜你的业务逻辑, 而是用 AST 把逻辑“翻译”成它能懂的语言。
通过 AST 自动提取 Go 方法的业务规则, 你不仅提升了测试效率,更在构建 AI 原生的质量基础设施。
现在,就去跑一遍你的核心函数—— 看看 AI 能不能真正“读懂”你的 if-else!
作者:Go 语言质量工程师
环境:Go 1.22 + 标准库 go/ast
完整代码:https://github.com/shemiouwang-lgtm/go-ast-rule-extractor.git