如何为Golang text/template编写全覆盖单元测试?
如何为Go text/template编写全覆盖单元测试?
嘿,我来帮你搞定Go text/template的单元测试和100%覆盖率问题!先把你的模板代码贴出来,咱们一步步拆解:
const tmpl = `#!/bin/bash {{- range .File.Dependency}} echo {{.EchoText}} {{- range .Install}} submitting {{.name}} {{- end}} {{.TypeCommand}} {{end}} {{- range $k, $v := .API}} echo {{$k}} submitting {{$v}} {{end}} `
第一步:定义匹配模板的数据结构
模板里用到了.File.Dependency、.API这些层级字段,咱们得先写出对应的Go结构体,注意字段要大写导出(不然模板访问不到),如果模板里用的是小写字段名,要加template标签映射,比如.name对应结构体的Name字段:
type TemplateData struct { File struct { Dependency []Dependency } API map[string]string } type Dependency struct { EchoText string Install []Install TypeCommand string } type Install struct { Name string `template:"name"` // 映射模板里的.name }
第二步:编写覆盖所有分支的测试用例
要达到100%覆盖率,得覆盖模板里的所有分支:两个range循环的进入/不进入场景,以及嵌套range的进入/不进入场景。咱们用testing包写测试,每个用例对应一种场景:
package yourpackage // 替换成你的包名 import ( "bytes" "testing" "text/template" ) // 这里放上面的const tmpl和结构体定义 func TestTemplateRendering(t *testing.T) { // 先解析模板,解析失败直接终止测试 tpl, err := template.New("test").Parse(tmpl) if err != nil { t.Fatalf("解析模板失败:%v", err) } // 定义测试用例,覆盖所有可能的场景 testCases := []struct { name string data TemplateData expected string }{ { name: "空数据(两个range都不执行)", data: TemplateData{}, expected: "#!/bin/bash ", }, { name: "单个Dependency无Install,带API(外层range执行,嵌套range不执行,API range执行)", data: TemplateData{ File: struct { Dependency []Dependency }{ Dependency: []Dependency{ { EchoText: "hello-world", TypeCommand: "apt install", }, }, }, API: map[string]string{"auth": "v1"}, }, expected: "#!/bin/bash echo hello-world apt install echo auth submitting v1 ", }, { name: "单个Dependency带多个Install,无API(外层和嵌套range都执行,API range不执行)", data: TemplateData{ File: struct { Dependency []Dependency }{ Dependency: []Dependency{ { EchoText: "setup-env", Install: []Install{ {Name: "pkg1"}, {Name: "pkg2"}, }, TypeCommand: "yum install", }, }, }, }, expected: "#!/bin/bash echo setup-env submitting pkg1 submitting pkg2 yum install ", }, { name: "多个Dependency混合(有的带Install有的不带),多API条目(覆盖循环多次执行的场景)", data: TemplateData{ File: struct { Dependency []Dependency }{ Dependency: []Dependency{ { EchoText: "prep-system", TypeCommand: "brew update", }, { EchoText: "deploy-service", Install: []Install{ {Name: "backend"}, }, TypeCommand: "systemctl start", }, }, }, API: map[string]string{"user": "v2", "pay": "v3"}, }, expected: "#!/bin/bash echo prep-system brew update echo deploy-service submitting backend systemctl start echo user submitting v2 echo pay submitting v3 ", }, } // 遍历执行每个测试用例 for _, tc := range testCases { t.Run(tc.name, func(t *testing.T) { var buf bytes.Buffer // 渲染模板到缓冲区 err := tpl.Execute(&buf, tc.data) if err != nil { t.Errorf("用例[%s]执行失败:%v", tc.name, err) return } // 对比输出和预期结果 if buf.String() != tc.expected { t.Errorf("用例[%s]结果不符:预期「%s」,实际「%s」", tc.name, tc.expected, buf.String()) } }) } }
第三步:验证覆盖率并补全
写完测试后,运行以下命令查看覆盖率:
go test -cover
如果没到100%,生成覆盖详情文件:
go test -coverprofile=cover.out
再用浏览器打开查看具体哪行没覆盖:
go tool cover -html=cover.out
比如如果发现某个分支没覆盖,就补充对应的测试用例(比如多Dependency里有一个带3个Install的情况,或者API为空但Dependency有多个的情况),直到所有行都被覆盖。
内容的提问来源于stack exchange,提问作者user8557463
相关产品推荐
相关产品推荐

