Go语言开发(十九)、GoConvey测试框架
一、GoConvey简介
1、GoConvey简介
GoConvey是一款针对Golang的测试框架,可以管理和运行测试用例,同时提供了丰富的断言函数,并支持多种Web界面特性。
官方地址:
https://github.com/smartystreets/goconvey
安装:
go get github.com/smartystreets/goconvey
2、GoConvey的特点
GoConvey支持 go test,可直接在终端窗口和浏览器上使用。GoConvey特点如下:
A、直接集成go test
B、巨大的回归测试套件
C、可读性强的色彩控制台输出
D、完全自动化的Web UI
E、测试代码生成器
二、GoConvey测试用例编写
1、GoConvey标准断言
官方文档:
https://github.com/smartystreets/goconvey/wiki
GoConvey自带大量的标准断言函数,可以通过So()使用。
(1)通用相等比较
So(thing1,ShouldEqual,thing2)
So(thing1,ShouldNotEqual,ShouldResemble,thing2)
用于数组、切片、map和结构体的深度比较
So(thing1,ShouldNotResemble,ShouldPointTo,ShouldNotPointTo,ShouldBeNil)
So(thing1,ShouldNotBeNil)
So(thing1,ShouldBeTrue)
So(thing1,ShouldBeFalse)
So(thing1,ShouldBeZeroValue)
(2)数值比较
So(1,ShouldBeGreaterThan,0)
So(1,ShouldBeGreaterThanOrEqualTo,ShouldBeLessThan,2)
So(1,ShouldBeLessThanOrEqualTo,2)
So(1.1,ShouldBeBetween,.8,1.2)
So(1.1,ShouldNotBeBetween,2,3)
So(1.1,ShouldBeBetweenOrEqual,.9,1.1)
So(1.1,ShouldNotBeBetweenOrEqual,1000,2000)
So(1.0,ShouldAlmostEqual,0.99999999,.0001)
带容差比较,默认容差为0.0000000001
So(1.0,ShouldNotAlmostEqual,0.9,.0001)
(3)数据集合比较
So([]int{2,4,6},ShouldContain,4)
So([]int{2,ShouldNotContain,5)
So(4,ShouldBeIn,...[]int{2,6})
So(4,ShouldNotBeIn,...[]int{1,3,5})
So([]int{},ShouldBeEmpty)
So([]int{1},ShouldNotBeEmpty)
So(map[string]string{"a": "b"},ShouldContainKey,"a")
So(map[string]string{"a": "b"},ShouldNotContainKey,"b")
So(map[string]string{"a": "b"},ShouldNotBeEmpty)
So(map[string]string{},ShouldBeEmpty)
So(map[string]string{"a": "b"},ShouldHaveLength,1)
支持map、切片、通道、字符串
(4)字符串比较
So("asdf",ShouldStartWith,"as")
So("asdf",ShouldNotStartWith,"df")
So("asdf",ShouldEndWith,ShouldNotEndWith,ShouldContainSubstring,"sd")
So("asdf",ShouldNotContainSubstring,"er")
So("adsf",ShouldBeBlank)
So("asdf",ShouldNotBeBlank)
(5)异常比较
So(func(),ShouldPanic)
So(func(),ShouldNotPanic)
So(func(),ShouldPanicWith,"") // or errors.New("something")
So(func(),ShouldNotPanicWith,"") // or errors.New("something")
(6)类型检查
So(1,ShouldHaveSameTypeAs,ShouldNotHaveSameTypeAs,"asdf")
(7)时间比较
So(time.Now(),ShouldHappenBefore,time.Now())
So(time.Now(),ShouldHappenOnOrBefore,ShouldHappenAfter,ShouldHappenOnOrAfter,ShouldHappenBetween,time.Now(),ShouldHappenOnOrBetween,ShouldNotHappenOnOrBetween,ShouldHappenWithin,duration,ShouldNotHappenWithin,time.Now())
2、测试用例编写
官方推荐使用导入GoConvey的辅助包以减少冗余的代码:
. "github.com/smartystreets/goconvey/convey"
GoConvey包导入在工程代码中如下:
import (
"testing"
. "github.com/smartystreets/goconvey/convey"
)
每个单元测试的名称需以?Test?开头,并需要接收一个类型为?*testing.T?
的参数。
每个测试用例需要使用Convey函数包裹起来,第一个参数为string类型的测试用例描述;第二个参数一般为?*testing.T
;第三个参数为不接收任何参数也不返回任何值的函数(通常以闭包的形式书写)。
Convey语句可以无限嵌套,以体现各个测试用例之间的关系,只有最外层的?Convey?需要传入*testing.T
类型变量,内层嵌套的Convey不需要传入。
func TestAdd(t *testing.T) {
Convey("将两数相加",t,func() {
So(Add(1,2),3)
})
}
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}表示测试中实际已运行的断言语句数。
3、测试用例示例
Operator.go文件:
package Operator
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("Divisor is 0")
}
return a / b,nil
}
Operator_test.go文件:
package Operator
import (
"testing"
. "github.com/smartystreets/goconvey/convey"
)
func TestAdd(t *testing.T) {
Convey("将两数相加",3)
})
}
func TestSubtract(t *testing.T) {
Convey("将两数相减",func() {
So(Subtract(1,-1)
})
}
func TestMultiply(t *testing.T) {
Convey("将两数相乘",func() {
So(Multiply(3,6)
})
}
func TestDivision(t *testing.T) {
Convey("将两数相除",func() {
Convey("除以非 0 数",func() {
num,err := Division(10,2)
So(err,ShouldBeNil)
So(num,5)
})
Convey("除以 0",func() {
_,0)
So(err,ShouldNotBeNil)
})
})
}
4、定制断言函数
So的函数原型如下:
func So(actual interface{},assert assertion,expected ...interface{})
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)."
)
定制断言函数如下:
func ShouldSummerBeComming(actual interface{},expected ...interface{}) string {
if actual == "summer" && expected[0] == "comming" {
return ""
} else {
return "summer is not comming!"
}
}
单元测试如下:
func TestSummer(t *testing.T) {
Convey("TestSummer",func() {
So("summer",ShouldSummerBeComming,"comming")
So("winter","comming")
})
}
根据ShouldSummerBeComming的实现,闭包中第一个So将断言成功,第二个So将断言失败。
三、测试结果查看
1、命令行测试结果查看
GoConvey兼容Go原生的单元测试,可以直接使用Go命令来执行测试。
在测试代码目录下运行go test命令:
?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 GoExample/GoConvey 0.002s
2、Web界面测试结果查看
查看goconvey用法
goconvey -h
-cover:开启覆盖率统计功能
-depth int:扫描目录的深度,-1:扫描无穷深度,0:扫描当前目录
-excludedDirs string:将某些目录排除在扫描外,多个目录使用逗号分割
-host string:指定开启HTTP服务的主机
-launchBrowser:触发自动开启浏览器,默认为true
-port int:指定HTTP服务的端口
-workDir string:指定工作目录,默认为当前目录
在测试用例源码目录下运行goconvey:
goconvey -port 8081
在浏览器打开:
http://localhost:8081
结果如下:
