分享
  1. 首页
  2. 文章

如何得知golang代码覆盖率

Andy · · 5809 次点击 · · 开始浏览
这是一个创建于 的文章,其中的信息可能已经有所发展或是发生改变。

以前写程序时,很少关注单元测试。 即便写,也是草草了事。没有很认真的写过,更别谈统计代码覆盖率了。出现这种情况固然是不对的, 但也跟市面上缺乏准确有效的代码覆盖率统计工具有一丝关系吧。

golang的出现对这种局面有了一些改变。 golang定位于工程化语言,所以其也自带了一部分有用的辅助工具。而cover tool就是其中一个很有用的代码覆盖率统计工具。

golang的cover工具基本原理和其它工具类似,都是在原始代码中寻找分支,然后在每个分支"种下"锚点。 等所有的case都跑完后,通过统计执行锚点的数量来计算覆盖率。

假如我们有如下的代码:

package size
func Size(a int) string { 
 switch {
 case a < 0:
 return "negative"
 case a == 0:
 return "zero"
 case a < 10:
 return "small"
 case a < 100:
 return "big"
 case a < 1000:
 return "huge"
 }
 return "enormous"
}

golang在分析完分支之后,种下锚点:

func Size(a int) string { 
 GoCover.Count[0] = 1
 switch {
 case a < 0:
 GoCover.Count[2] = 1
 return "negative"
 case a == 0:
 GoCover.Count[3] = 1
 return "zero"
 case a < 10:
 GoCover.Count[4] = 1
 return "small"
 case a < 100:
 GoCover.Count[5] = 1
 return "big"
 case a < 1000:
 GoCover.Count[6] = 1
 return "huge"
 }
 GoCover.Count[1] = 1
 return "enormous"
}

这样当所有case运行完后,通过计算GoCover的Count数量来计算覆盖率。以上这些工作都是golang自己完成,而不需要用户参与。

我们用上一篇出现的mongo unit test code来做demo,看看cover tool如何使用。 业务代码如下:

package main
import ( 
 "fmt"
 "log"
 "time"
 mgo "gopkg.in/mgo.v2"
)
type dpi struct { 
 Area string `json:"area`
 Count int `json:"count"`
}
func GetMongo() *mgo.Session { 
 login := &mgo.DialInfo{
 Addrs: []string{"10.50.1.60:1301"},
 Timeout: 3600 * time.Second,
 Database: "DPI_URL",
 }
 log.Printf("Connectting mongodb, host:[%s] db:[%s]\n", login.Addrs, login.Database)
 mgoSession, err := mgo.DialWithInfo(login)
 if err != nil {
 fmt.Println(err.Error())
 }
 if err := mgoSession.Ping(); err == nil {
 log.Printf("MONGO CONNECT SUCCESS! [%s]\n", mgoSession.LiveServers())
 } else {
 log.Printf("MONGO CONNECT FAILED!! [10.50.1.60:1301] Error Info [%s]\n", err.Error())
 }
 session := mgoSession.Clone()
 if session == nil {
 fmt.Println("MONGODB SESSION IS NIL!!")
 return nil
 }
 return session
}
func main() { 
 session := GetMongo()
 c := session.DB("DPI_URL").C("DATA_COUNT")
 ds := getDPI(c)
 for _, d := range ds {
 log.Println(d)
 }
}
func getDPI(c *mgo.Collection) []dpi { 
 var ds []dpi
 err := c.Find(nil).All(&ds)
 if err != nil {
 log.Println(err.Error())
 }
 return ds
}

Unit test 代码如下:

package main
import ( 
 "log"
 "testing"
 "gopkg.in/check.v1"
 mgo "gopkg.in/mgo.v2"
 "gopkg.in/mgo.v2/dbtest"
)
var col *mgo.Collection 
var server dbtest.DBServer 
var session *mgo.Session
var ds = []interface{}{ 
 dpi{Area: "a1", Count: 100},
 dpi{Area: "a2", Count: 200},
 dpi{Area: "a3", Count: 300},
}
func init() { 
 server.SetPath("/tmp")
 session = server.Session()
 if session == nil {
 log.Panicln("Can not get mongo session")
 }
 col = session.DB("tdb").C("tc")
 col.RemoveAll(nil)
}
type S struct{}
var _ = check.Suite(&S{})
func TestAll(t *testing.T) { 
 check.TestingT(t)
}
func (s *S) TestGetDpi(c *check.C) { 
 defer func() {
 session.Close()
 server.Stop()
 }()
 col.Insert(ds...)
 dv := getDPI(col)
 if len(dv) != 3 {
 c.Error("dv size is error")
 }
 if dv[0].Area != "a1" || dv[1].Area != "a2" || dv[2].Area != "a3" {
 c.Error("dv is error")
 }
 log.Println("OK")
}

现在来看看代码覆盖率:

root@d206835c4fc7:/go/src/mongo# go test -cover 
2017年04月26日 07:02:24 OK 
OK: 1 passed 
PASS 
coverage: 17.4% of statements 
ok mongo 0.708s 
17.4%.... 很低的一个覆盖率。 如何提升覆盖率呢? 这个问题golang也想到了,所以golang会给我们一些更为有用的信息。 

下面让golang来收集更多的覆盖信息:

go test -coverprofile=coverage.out 
2017年04月26日 07:00:15 OK 
OK: 1 passed 
PASS 
coverage: 17.4% of statements 
ok mongo 0.748s 

执行完之后,没有输出更多有用的信息。 那会不会保存在coverage.out里面了呢?

root@d206835c4fc7:/go/src/mongo# more coverage.out 
mode: set 
mongo/main.go:16.30,24.16 4 0 
mongo/main.go:28.2,28.42 1 0 
mongo/main.go:34.2,35.20 2 0 
mongo/main.go:40.2,40.16 1 0 
mongo/main.go:24.16,26.3 1 0 
mongo/main.go:28.42,30.3 1 0 
mongo/main.go:30.3,32.3 1 0 
mongo/main.go:35.20,38.3 2 0 
mongo/main.go:43.13,50.23 4 0 
mongo/main.go:50.23,52.3 1 0 
mongo/main.go:55.38,58.16 3 1 
mongo/main.go:62.2,62.11 1 1 
mongo/main.go:58.16,60.3 1 0 

好吧,完全看不懂。 换种格式来看看:

go tool cover -func=coverage.out 
mongo/main.go:16: GetMongo 0.0% 
mongo/main.go:43: main 0.0% 
mongo/main.go:55: getDPI 80.0% 
total: (statements) 17.4% 

如此一来,我们就知道了。 只有getDPI函数覆盖了80%,其它两个函数丁点没有覆盖,因此加权之后就是17.4%了。getDPI没有覆盖的代码是哪些呢? golang同样也可以告诉你:

go tool cover -html=coverage.out 
HTML output written to /tmp/cover819344406/coverage.html 

可以看到在getDPI函数中,log.Println(err.Error())被标红处理了,也就是说这行没有被覆盖到。 同理上面所有标红的代码都没有被覆盖到。

写到这里,基本上golang cover工具就够我们使用了。 但我们探索的脚本还不想停下来,看看cover还有哪些潜力。因为golang的cover工具是基于源码进行统计的,那么它能不能统计递归次数呢?也就是看看每个函数的heat maps。

golang使用-covermode来接受用户的统计指令:

  • set: 统计每个函数是否都执行了
  • count:统计每个函数执行了多少次
  • atomic:和count作用类似,但是用在并发场景中统计执行次数。

默认情况下,我们使用的都是set。 下面我们用fmt来演示一下使用count的结果:

go test -covermode=count -coverprofile=count.out fmt 
ok fmt 0.056s coverage: 91.7% of statements 
不亏是golang官方包,91.7的代码覆盖率。。。
go tool cover -func=count.out 
fmt/format.go: init 100.0% 
fmt/format.go: clearflags 100.0% 
fmt/format.go: init 100.0% 
fmt/format.go: computePadding 84.6% 
fmt/format.go: writePadding 100.0% 
fmt/format.go: pad 100.0% 
...
fmt/scan.go: advance 96.2% 
fmt/scan.go: doScanf 96.8% 
total: (statements) 91.7% 

用一种直观的方式表示一下:

go tool cover -html=count.out

越绿的代码就表示调用次数越高,把鼠标移到上面就可以看到每行代码的调用次数。

以上就是我对golang test cover工具的一些使用心得,希望能对你有所帮助。 如果你有什么想交流的,不要吝啬您的留言。


有疑问加站长微信联系(非本文作者)

本文来自:Andy's Blog

感谢作者:Andy

查看原文:如何得知golang代码覆盖率

入群交流(和以上内容无关):加入Go大咖交流群,或添加微信:liuxiaoyan-s 备注:入群;或加QQ群:692541889

关注微信
5809 次点击
1 回复 | 直到 2025年04月29日 16:47:42
暂无回复
添加一条新回复 (您需要 后才能回复 没有账号 ?)
  • 请尽量让自己的回复能够对别人有帮助
  • 支持 Markdown 格式, **粗体**、~~删除线~~、`单行代码`
  • 支持 @ 本站用户;支持表情(输入 : 提示),见 Emoji cheat sheet
  • 图片支持拖拽、截图粘贴等方式上传

用户登录

没有账号?注册
(追記) (追記ここまで)

今日阅读排行

    加载中
(追記) (追記ここまで)

一周阅读排行

    加载中

关注我

  • 扫码关注领全套学习资料 关注微信公众号
  • 加入 QQ 群:
    • 192706294(已满)
    • 731990104(已满)
    • 798786647(已满)
    • 729884609(已满)
    • 977810755(已满)
    • 815126783(已满)
    • 812540095(已满)
    • 1006366459(已满)
    • 692541889

  • 关注微信公众号
  • 加入微信群:liuxiaoyan-s,备注入群
  • 也欢迎加入知识星球 Go粉丝们(免费)

给该专栏投稿 写篇新文章

每篇文章有总共有 5 次投稿机会

收入到我管理的专栏 新建专栏