
Pass、Failutil,重点覆盖xx_test.go命名TestXXX开头t *testing.Thttps://golang.google.cn/pkg/testing/
Go提供了test工具用于代码的单元测试,test工具会查找包下以_test.go结尾的文件,调用测试文件中以 Test或Benchmark开头的函数并给出运行结果
测试函数需要导入testing包,并定义以Test开头的函数,参数为testing.T指针类型,在测试函数中调用函数进行返回值测试,当测试失败可通过testing.T结构体的Error函数抛出错误

单元测试是对某个功能的测试 命令行执行
go test 包名 # 测试整个包
go test -v .
go test 包名/文件名 # 测试某个文件简单使用
准备待测代码compute.go
package pkg03
func Add(a, b int) int {
return a + b
}
func Mul(a, b int) int {
return a * b
}
func Div(a, b int) int {
return a / b
}准备测试用例compute_test.go
package pkg03
import "testing"
func TestAdd(t *testing.T) {
a := 10
b := 20
want := 30
actual := Add(a, b)
if want != actual {
t.Errorf("Add函数参数:%d %d, 期望: %d, 实际: %d", a, b, want, actual)
}
}
func TestMul(t *testing.T) {
a := 10
b := 20
want := 300
actual := Mul(a, b)
if want != actual {
t.Errorf("Mul函数参数:%d %d, 期望: %d, 实际: %d", a, b, want, actual)
}
}
func TestDiv(t *testing.T) {
a := 10
b := 20
want := 2
actual := Div(a, b)
if want != actual {
t.Errorf("Div函数参数:%d %d, 期望: %d, 实际: %d", a, b, want, actual)
}
}执行测试
➜ pwd
golang-learning/chapter06/pkg03
➜ go test -v .
=== RUN TestAdd
--- PASS: TestAdd (0.00s)
=== RUN TestMul
compute_test.go:21: Mul函数参数:10 20, 期望: 300, 实际: 200
--- FAIL: TestMul (0.00s)
=== RUN TestDiv
compute_test.go:31: Div函数参数:10 20, 期望: 2, 实际: 0
--- FAIL: TestDiv (0.00s)
FAIL
FAIL pkg03 0.198s
FAIL只执行某个函数
go test -run=TestAdd -v .
=== RUN TestAdd
--- PASS: TestAdd (0.00s)
PASS
ok pkg03 0.706s正则过滤函数名
go test -run=TestM.* -v .用于统计目标包有百分之多少的代码参与了单测
使用go test工具进行单元测试并将测试覆盖率覆盖分析结果输出到cover.out文件
例如上面的例子
go test -v -cover
=== RUN TestAdd
--- PASS: TestAdd (0.00s)
=== RUN TestMul
compute_test.go:21: Mul函数参数:10 20, 期望: 300, 实际: 200
--- FAIL: TestMul (0.00s)
=== RUN TestDiv
compute_test.go:31: Div函数参数:10 20, 期望: 2, 实际: 0
--- FAIL: TestDiv (0.00s)
FAIL
coverage: 100.0% of statements
exit status 1
FAIL pkg03 0.185s生成测试覆盖率文件
go test -v -coverprofile=cover.out
=== RUN TestAdd
--- PASS: TestAdd (0.00s)
=== RUN TestAddFlag
--- PASS: TestAddFlag (0.00s)
PASS
coverage: 75.0% of statements
ok testcalc/calc 0.960s
分析测试结果,打开测试覆盖率结果文件,查看测试覆盖率
go tool cover -html cover.outfunc TestMul2(t *testing.T) {
t.Run("正数", func(t *testing.T) {
if Mul(4, 5) != 20 {
t.Fatal("muli.zhengshu.error")
}
})
t.Run("负数", func(t *testing.T) {
if Mul(2, -3) != -6 {
t.Fatal("muli.fushu.error")
}
})
}执行测试
➜ go test -v .
=== RUN TestMul2
=== RUN TestMul2/正数
=== RUN TestMul2/负数
--- PASS: TestMul2 (0.00s)
--- PASS: TestMul2/正数 (0.00s)
--- PASS: TestMul2/负数 (0.00s)指定func/sub运行子测试
➜ go test -run=TestMul2/正数 -v
=== RUN TestMul2
=== RUN TestMul2/正数
--- PASS: TestMul2 (0.00s)
--- PASS: TestMul2/正数 (0.00s)
PASS
ok pkg03 0.675s子测试的作用:table-driven tests
cases中,看起来就像一张表,借助循环创建子测试。这样写的好处有
cases新增一条测试数据即可goconvey是一个第三方测试框架,其最大好处就是对常规的if else进行了高度封装
准备待测代码student.go
package pkg04
import "fmt"
type Student struct {
Name string
ChiScore int
EngScore int
MathScore int
}
func NewStudent(name string) (*Student, error) {
if name == "" {
return nil, fmt.Errorf("name为空")
}
return &Student{
Name: name,
}, nil
}
func (s *Student) GetAvgScore() (int, error) {
score := s.ChiScore + s.EngScore + s.MathScore
if score == 0 {
return 0, fmt.Errorf("全都是0分")
}
return score / 3, nil
}参考官方示例,准备测试用例student_test.go
直观来讲,使用goconvey的好处是不用再写多个if判断
package pkg04
import (
. "github.com/smartystreets/goconvey/convey"
"testing"
)
func TestNewStudent(t *testing.T) {
Convey("start test new", t, func() {
stu, err := NewStudent("")
Convey("空的name初始化错误", func() {
So(err, ShouldBeError)
})
Convey("stu对象为nil", func() {
So(stu, ShouldBeNil)
})
})
}
func TestScore(t *testing.T) {
stu, _ := NewStudent("hh")
Convey("不设置分数可能出错", t, func() {
sc, err := stu.GetAvgScore()
Convey("获取分数出错了", func() {
So(err, ShouldBeError)
})
Convey("分数为0", func() {
So(sc, ShouldEqual, 0)
})
})
Convey("正常情况", t, func() {
stu.ChiScore = 60
stu.EngScore = 70
stu.MathScore = 80
score, err := stu.GetAvgScore()
Convey("获取分数出错了", func() {
So(err, ShouldBeNil)
})
Convey("平均分大于60", func() {
So(score, ShouldBeGreaterThan, 60)
})
})
}执行go test -v .
➜ go test -v .
=== RUN TestNewStudent
start test new
空的name初始化错误 ✔
stu对象为nil ✔
2 total assertions
--- PASS: TestNewStudent (0.00s)
=== RUN TestScore
不设置分数可能出错
获取分数出错了 ✔
分数为0 ✔
4 total assertions
正常情况
获取分数出错了 ✔
平均分大于60 ✔
6 total assertions
--- PASS: TestScore (0.00s)
PASS
ok pkg04 0.126sgoconvey的二进制go get github.com/smartystreets/goconvey
# 会将对应的二进制文件放到 $GOPATH/bin 下面GOPATH/bin加入PATH里面 或者写全路径goconvey,启动http 8000,自动运行测试用例最终效果如下

业务代码cal.go
package pkg05
func Add(x int ) (result int) {
result = x + 2
return result
}测试用例cal_test.go
package pkg05
import (
"github.com/stretchr/testify/assert"
"testing"
)
func TestAdd(t *testing.T) {
// assert equality
assert.Equal(t, Add(5), 7, "they should be equal")
}执行测试
➜ go test -v .
=== RUN TestAdd
--- PASS: TestAdd (0.00s)
PASS
ok pkg05 1.216spackage pkg05
import (
"github.com/stretchr/testify/assert"
"testing"
)
func TestAdd(t *testing.T) {
// assert equality
assert.Equal(t, Add(5), 7, "they should be equal")
}
func TestCal(t *testing.T) {
ass := assert.New(t)
var tests = []struct {
input int
expected int
}{
{2, 4},
{-1, 1},
{0, 2},
{-5, -3},
{999999997, 999999999},
}
for _, test := range tests {
ass.Equal(Add(test.input), test.expected)
}
}testify/mock隔离第三方依赖或者复杂调用testfiy/mock使得伪造对象的输入输出值可以在运行时决定https://github.com/m3db/m3/pull/3525
