Go 语言虽然自带单元测试功能,在 GoConvey 诞生之前也出现了许多第三方辅助库。但没有一个辅助库能够像 GoConvey 这样优雅地书写代码的单元测试,简洁的语法和舒适的界面能够让一个不爱书写单元测试的开发人员从此爱上单元测试。
下载:go get github.com/smartystreets/goconvey
- 在$GOPATH/src目录下新增了github.com子目录,该子目录里包含了GoConvey框架的库代码
- 在$GOPATH/bin目录下新增了GoConvey框架的可执行程序goconvey
下面是一个能够实现整数基本四则运算(加、减、乘、除)的代码:
package main
import "errors"
func Add(a, b int) int {
return a + b
}
func Subtract(a, b int) int {
return a - b
}
func Multiply(a, b int) int {
return a * b
}
func Division(a, b int) (int, error) {
if b == 0 {
return 0, errors.New("被除数不能为 0")
}
return a / b, nil
}
在上面的代码中,我们实现了 4 个函数,因此需要为这 4 个函数分别书写单元测试:
文件名要以 _test.go 结尾
在本例中,我们只使用了 3 个不同类型的条件判断:ShouldBeNil
、ShouldEqual
和 ShouldNotBeNil
,分别表示值应该为 nil、值应该相等和值不应该为 nil。有关详细的条件列表,可以参见 官方文档
package main
import (
. "github.com/smartystreets/goconvey/convey"
"testing"
)
func TestAdd(t *testing.T) {
Convey("将两数相加", t, func() {
So(Add(1, 2), ShouldEqual, 3)
})
}
func TestSubtract(t *testing.T) {
Convey("将两数相减", t, func() {
So(Subtract(1, 2), ShouldEqual, -1)
})
}
func TestMultiply(t *testing.T) {
Convey("将两数相乘", t, func() {
So(Multiply(3, 2), ShouldEqual, 6)
})
}
func TestDivision(t *testing.T) {
Convey("将两数相除", t, func() {
Convey("除以非 0 数", func() {
num, err := Division(10, 2)
So(err, ShouldBeNil)
So(num, ShouldEqual, 5)
})
Convey("除以 0", func() {
_, err := Division(10, 0)
So(err, ShouldNotBeNil)
})
})
}
测试: go test -v
=== RUN TestAdd
将两数相加 .
1 total assertion
--- PASS: TestAdd (0.00s)
=== RUN TestSubtract
将两数相减 .
2 total assertions
--- PASS: TestSubtract (0.00s)
=== RUN TestMultiply
将两数相乘 .
3 total assertions
--- PASS: TestMultiply (0.00s)
=== RUN TestDivision
将两数相除
除以非 0 数 ..
除以 0 .
6 total assertions
--- PASS: TestDivision (0.00s)
PASS
ok study/testing 0.057s
我们可以看到,输出结果调理非常清晰,单元测试的代码写起来也非常优雅。那么,这就是全部吗?当然不是。GoConvey 不仅支持在命令行进行人工调用调试命令,还有非常舒适的 Web 界面提供给开发者来进行自动化的编译测试工作。
web界面
想要使用 GoConvey 的 Web 界面特性,需要在相应目录下执行 goconvey
(需使用 go get
安装到 $GOPATH/bin
目录下),然后打开浏览器,访问 http://localhost:8080 ,就可以看到下以下界面:
Skip
针对想忽略但又不想删掉或注释掉某些断言操作,GoConvey提供了Convey/So的Skip方法:
- SkipConvey函数表明相应的闭包函数将不被执行
- SkipSo函数表明相应的断言将不被执行
当存在SkipConvey或SkipSo时,测试日志中会显式打上”skipped”形式的标记:
- 当测试代码中存在SkipConvey时,相应闭包函数中不管是否为SkipSo,都将被忽略,测试日志中对应的符号仅为一个”⚠”
- 当测试代码Convey语句中存在SkipSo时,测试日志中每个So对应一个”✔”或”✘”,每个SkipSo对应一个”⚠”,按实际顺序排列
- 不管存在SkipConvey还是SkipSo时,测试日志中都有字符串”{n} total assertions (one or more sections skipped)”,其中{n}表示测试中实际已运行的断言语句数
定制断言函数
我们先看一下So的函数原型:
func So(actual interface{}, assert assertion, expected ...interface{})
第二个参数为assertion,它的原型为:
type assertion func(actual interface{}, expected ...interface{}) string
当assertion的返回值为””时表示断言成功,否则表示失败,GoConvey框架中的相关代码为:
const (
success = ""
needExactValues = "This assertion requires exactly %d comparison values (you provided %d)."
needNonEmptyCollection = "This assertion requires at least 1 comparison value (you provided 0)."
)
新建:other_test.go
package main
import (
"fmt"
. "github.com/smartystreets/goconvey/convey"
"testing"
"time"
)
func ShouldGt(actual interface{}, expected ...interface{}) string {
if len(expected) == 0 {
return "缺少参数 expected..."
}
val1 := actual.(int)
val2 := expected[0].(int)
if val1 <= val2 {
return fmt.Sprintf("%d 不大于 %d", actual, expected[0])
}
return ""
}
func TestGt(t *testing.T) {
time.Sleep(time.Second * 3) //仅仅是测试耗时
Convey("两数相乘是否大于100", t, func() {
So(90, ShouldGt, 100)
So(110, ShouldGt, 100)
})
}
测试:go test -v other_test.go
=== RUN TestGt
两数相乘是否大于100 ..
2 total assertions
--- PASS: TestGt (3.00s)
PASS
ok command-line-arguments 3.066s
C:\Users\user\Desktop\GOProject\src\study\testing>go test -v other_test.go
=== RUN TestGt
两数相乘是否大于100 x
Failures:
* C:/Users/user/Desktop/GOProject/src/study/testing/other_test.go
Line 33:
90 不大于 100
....
1 total assertion
--- FAIL: TestGt (3.00s)
FAIL
FAIL command-line-arguments 3.055s
FAIL