测试

测试金字塔

最下面是单元测试,单元测试对代码进行测试。再而上是集成测试,它对一个服务的接口进行测试。继而是端到端的测试,我也称他为链路测试,它负责从一个链路的入口输入测试用例,验证输出的系统的结果。再上一层是我们最常用的UI测试,就是测试人员在UI界面上根据功能进行点击测试。

单元测试

所有以_test.go为后缀名的源代码文件都是go test测试的一部分,不会被go build编译到最终的可执行文件中,xxx_test.go测试文件代码和被测试的源代码文件xxx.go放在同一个包内,一一对应。

比如项目下有某个包内有个源代码文件xxx.go有这么一个函数:

那么单元测试的文件xxx_test.go就放在相同的包内,测试函数的名字必须以Test开头,可选的后缀名必须以大写字母开头,需要一个*testing.T类型的参数,可以像下面这样按组测试,又称表格测试法:

执行测试程序

在项目根目录下直接执行go test .含义是:仅从当前目录下查找以_test.go为后缀的以Test开头的单元测试函数。

go test ./...含义是:找出当前的目录以及所有子目录(包括多级子目录)下所有的 xxx_test.go 测试代码。

go test ./xxx/xxx含义是:找出./xxx/xxx路径下的 xxx_test.go 测试代码,如果想执行指定目录以及其子目录的单元测试,需要指定目录为:./xxx/xxx/...,即在路径后加上/...,注意是 3 个.

默认不执行基准测试,加上-bench参数来执行,如:go test -bench=.

测试项目某个模块下的指定单元测试和指定的基准测试,如:go test -v service/*.go -bench BenchMethodName -benchmem -run TestMethodName

MethodName可以写正则表达式匹配,比如如果只想执行benchmark,可以把-run参数写成none或者^$,不存在正则匹配的单元测试也就只会执行基准测试了。

基准测试

基准测试就是在一定的工作负载之下检测程序性能的一种方法。基准测试函数以Benchmark为前缀,需要一个*testing.B类型的参数。

可以为基准测试添加-benchmem参数,来获得内存分配的统计数据:

  • BenchmarkSubstr-8中数字8表示GOMAXPROCS的值,即并发的线程数(并发的协程比该值大)

  • 数字231的含义:是本次Benchmark循环体共执行的次数

  • 5152279 ns/op:本次Benchmark循环体每循环一次耗费5152279纳秒

  • 655590 B/op:本次Benchmark循环体每循环一次分配了655590字节

  • 2 allocs/op:本次Benchmark循环体每循环一次进行了2次内存分配

-benchtime=3s为基准测试限定执行时间。

benchstat

如果没有benchstat就先安装:go get golang.org/x/perf/cmd/benchstat

集成测试

集成测试和单元测试不一样,它不属于某个文件,集成测试可能涉及到多个文件中多个接口的测试,所以它需要放在一个单独的文件夹,完整项目

suite和上面的assert在同一个包下:github.com/stretchr/testify

假设有如下2个函数需要编写单元测试:

单元测试文件代码如下:

SetupSuite()、TearDownSuite() 仅执行一次,典型使用场景是各个单元测试共用的数据库连接

而 SetupTest()、BeforeTest()、AfterTest()、TearDownTest()对套件中的每个测试都会执行一次

测试覆盖率

  1. 取得coverprofilego test -v -coverprofile=coverage.out

  2. 查看:go tool cover -html=coverage.out

编译到服务器上执行单元测试

编译: CGO_ENABLED=0 GOOS=linux GOARCH=amd64 go test -c *.go -o testxxx

连上服务器后执行单元测试: ./testxxx -test.run TestXxx_Xxx

gomock

gomock是对interface方法功能的模拟。

通常有的功能的单元测试不好写,比如依赖了第三方服务提供的接口,但它还没实现好,此时单元测试的编写就需要mock第三方服务接口的调用。

通过mock还可以模拟异常场景。

安装

go install github.com/golang/mock/[email protected]

使用

新建person/male.go,写入如下内容:

新建user/user.go,写入以下内容:

生成mock代码: mockgen -source=./person/male.go -destination=./mock/male_mock.go -package=mock

新建user/user_test.go,写入如下内容:

文件目录:

gomonkey

上一节介绍的gomock是需要在设计了interface的前提下的mock,但如果没有设计interface,要怎么mock呢?此时就需要用到gomonkey了。

gomonkey的实现原理是通过reflect包实现,在运行时替换指定变量、指定函数。

注:gomonkey不是线程安全的,所以不要把它用到并发的单元测试中。

安装

go get github.com/agiledragon/gomonkey/[email protected]

Mac下直接使用gomonkey会有如下这样的错误抛出:

注意:使用gomonkey在执行单元测试时需要禁用内联go test -gcflags=all=-l,否则mock不会成功。

典型用法

// 把gomonkey.ApplyMethod的例子写上

Last updated

Was this helpful?