单元测试覆盖率
- 测试覆盖率
go test -cover
结果类似于:
PASS
coverage: 90% of statements
ok github.com/sasakiyori/test 1.025s
- 区分unit test和integration test的覆盖率
参考: https://mickey.dev/posts/go-build-tags-testing/
unit test文件添加build tag
// +build !integration
integration test文件添加build tag
// +build integration
分别获取覆盖率:
# unit test
go test -cover
# integration test
go test --tags=integration -cover
- 测试覆盖率可视化
# 将测试结果输出为本地文件
go test -coverprofile=result_file
# 将测试结果按go function区分展示
go tool cover -func=result_file
# 将测试结果按html展示
go tool cover -html=result_file
# 将测试结果保存为html文件
go tool cover -html=result_file -o coverage.html
功能测试覆盖率
参考
https://medium.com/@manabie/test-coverage-of-go-services-during-integration-tests-6ff1bdbe33e0
代码实现
按go test的形式编译并启动可执行程序,手动触发daemon服务退出,即可统计整体覆盖率:
- 在main.go同级目录下创建文件
main_test.go
,主要实现TestRun
方法,内容与main.go中的main
方法一致 - 因为要生成覆盖率文件,需要让
TestRun
正常退出,因此使用context.CancelFunc
,在原服务之外另起一个killServer
,二者共享同一个context,通过调用killServer
触发其cancelFunc
,使原服务得到正常退出
package main
import (
"context"
"fmt"
"net/http"
"testing"
)
type killServer struct {
server http.Server
cancel context.CancelFunc
}
func newKillServer(addr string, cancel context.CancelFunc) *killServer {
return &killServer{
server: http.Server{
Addr: addr,
},
cancel: cancel,
}
}
func (s *killServer) Start() {
s.server.Handler = s
err := s.server.ListenAndServe()
if err != nil {
fmt.Println("KillServer Error:", err)
}
}
func (s *killServer) ServeHTTP(w http.ResponseWriter, r *http.Request) {
w.WriteHeader(http.StatusOK)
// cancel the context
s.cancel()
}
func TestRun(t *testing.T) {
ctx, cancel := context.WithCancel(context.Background())
killServer := newKillServer(":19999", cancel)
go killServer.Start()
// same call with func main()
go Run(ctx)
<-ctx.Done()
killServer.server.Shutdown(context.Background())
}
编译运行测试
main_test.go
编写完成后,运行命令生成可执行带测试文件xxx.test
。其中coverpkg
可以指定需要计算覆盖率的路径范围
go test -c ./ -cover -covermode=count -coverpkg=./...
- 运行测试文件,指定覆盖率输出文件
./xxx.test -test.coverprofile coverage.out
- 运行自己的功能测试,测试服务功能
- 测试完成后,调用
killServer
,退出测试程序,获得覆盖率,生成覆盖率输出文件curl 127.0.0.1:19999
- 查看覆盖率及输出文件
go tool cover -html=coverage.out
mock方法论
- 在不使用其他plugin/package的前提下的mock方法论, 包含sql, file, http call等: https://www.cbinsights.com/research/team-blog/mocking-techniques-for-go/
- Higher-order functions: 使用封装函数, 将需要mock的函数作为封装函数的参数, 达到可替换的效果
- Monkey patching: mock package级别的方法时, 将其赋予一个package级别的变量,再在mock时对变量进行替换
- interface substitution: 想mock某个interface时, mock一个struct完全实现interface的方法进行替换
- Embedding interfaces: 想mock某个interface里的部分函数时,可以将interface作为业务代码函数入参,使用内嵌的方式改写部分函数
- Mocking out downstream HTTP calls with net/http/httptest
- 好用的工具
PREVIOUSCasbin