go 覆盖率统计
被测服务是go服务,测试脚本是python等非go语言实现,可参考以下两种方法实现go服务的测试覆盖率统计。
方法一:go test 方式,不编译二进制文件
- 创建main_test.go文件
或者与你的 func main(){}方法所在的文件名同名的test文件。比如,有如下main.go文件:
package main
import (
"github.com/labstack/echo"
"hello-go/api"
)
func main() {
e := echo.New()
e.GET("/", api.HelloWorld)
e.GET("/api1", api.Api1)
e.GET("/api2", api.Api2)
e.Logger.Fatal(e.Start(":8001"))
}
根据以上main.go文件,创建以下main_test.go文件
package main
import (
"fmt"
"net/http"
"os"
"os/signal"
"testing"
)
var exitChan chan int
func testHandler(w http.ResponseWriter, req *http.Request) {
exitChan <- 666 }
func startServer() {
go main() // main.gomain
}
func TestExternal(t *testing.T) {
// start server need be tested in separate go thread
go startServer()
// go test starts a dummy http server, which is used to
// end the current go test gracefully when it's accessed.
http.HandleFunc("/", testHandler)
go http.ListenAndServe(":9999", nil)
// go test9999
// 9999
exitChan = make(chan int)
sigChann := make(chan os.Signal)
signal.Notify(sigChann, os.Interrupt)
select {
case sig := <-sigChann:
fmt.Printf("exit as received signal: %v\n", sig)
case val := <-exitChan:
fmt.Printf("exit as received http request: %v\n", val)
}
}
- 执行go test启动服务
进入main_test.go所在目录,一般在代码根目录,执行go test命令:
go test -coverprofile=cov.out -coverpkg ./... &
# -coverprofile 产出的覆盖率文件
# -coverpkg 要统计的覆盖率文件源码,可以指定文件
# & 后台执行
- 执行测试用例
curl 127.0.0.1:8001 #服务是否正常启动
curl 127.0.0.1:8001/api1 #测试用例1
curl 127.0.0.1:8001/api2 #测试用例2
curl 127.0.0.1:9999 #服务停止,注意:只有停止了服务才能生成覆盖率文件
- 生成覆盖率报告
在代码根目录会生成cov.out覆盖率文件。为了方便查看和浏览,可将out文件转换为html报告进行查看,执行命令如下:
go tool cover -html cov.out -o index.html
- 拷贝index.html到本地可查看代码行覆盖情况
方法二:编译出二进制文件执行
- 生成覆盖率二进制文件的原理介绍(插桩产物)
要运行系统测试,需要应用程序的编译二进制文件。然后,在具有不同配置的不同环境中执行此二进制文件。Golang提供了一种独特的方法来生成 覆盖率二进制文件,而不是go build生成的默认二进制文件。
生成的代码覆盖率二进制文件在每一行代码后写入一个唯一的计数器,并检查在执行二进制文件后调用此计数器的次数。
更多的技术细节可以在go-cover文档中找到。go cover 官方文档:https://blog.golang.org/cover
当执行go test时,覆盖率二进制文件会自动生成并在之后处理。Golang允许使用以下命令生成此覆盖率二进制文件:
go test -c -covermode=count -coverpkg ./...
#
# -c 表示 生成测试二进制文件
# -covermode=count 表示 生成的二进制中包含覆盖率计数信息
# -o packagename.test 表示 覆盖率二进制文件名
# -coverpkg ./... 表示 要统计的覆盖率文件源码
更多的参数信息可以执行go test -help来查看。
- 创建main_test文件
现在我们知道了如何生成二进制文件,我们必须确保二进制文件将按预期执行。您的代码需要满足以下要求,才能按照预期生成二进制。
package中至少有一个*_test.go文件,否则不会生成二进制文件。建议创建main_test.go 文件,或者与你的func main(){}所在的文件名同 名的test文件,与上一个方法类似,需要创建一个main_test.go文件让go test来插桩
main_test.go 同上
该文件定义了一个systemTest标志,并包含一个调用main函数的测试用例。
运行测试二进制文件开始执行测试。在例子中,这意味着调用TestExternal,因为这是唯一的测试。运行TestExternal 意味着调用main(),它 将像普通二进制文件那样启动应用程序。这也就意味着运行测试产生的二进制文件与运行普通二进制文件相同,只是运行测试产生的二进制文件将 会跟踪覆盖率执行,也就是我们常说的打桩。
为了防止在运行单元测试时运行此测试,添加了命令行标志systemTest。如果未设置,则不会调用main()函数。而要运行系统测试,必须在执行测 试二进制文件期间通过附加-systemTest来设置标志。
- 生成插桩后的覆盖率二进制文件
在代码根目录执行以下命令:
go test -c -covermode=count -coverpkg ./... -o hello.test
执行完成后将生成一个 hello.test文件
- 以打桩二进制文件启动服务
//启动服务(打桩二进制)
./hello.test -systemTest -test.coverprofile cov.out &
-
执行测试case
-
生成覆盖率报告
执行完用例并停止服务后,在代码根目录下生成覆盖率文件cov.out
为了方便查看和浏览,可将out文件转换为html报告进行查看,执行命令如下:
go tool cover -html cov.out -o index.html
参考文档
https://www.elastic.co/cn/blog/code-coverage-for-your-golang-system-tests