用Convey嵌套的方法,将所有测试用例的Convey用一个大的Convey包裹起来,每个测试函数下只有一个大的Convey。比如下面的示例代码:
- import (
- "testing"
- . "github.com/smartystreets/goconvey/convey"
- )
-
- func TestStringSliceEqual(t *testing.T) {
- Convey("TestStringSliceEqual", t, func() {
- Convey("should return true when a != nil && b != nil", func() {
- a := []string{"hello", "goconvey"}
- b := []string{"hello", "goconvey"}
- So(StringSliceEqual(a, b), ShouldBeTrue)
- })
-
- Convey("should return true when a == nil && b == nil", func() {
- So(StringSliceEqual(nil, nil), ShouldBeTrue)
- })
-
- Convey("should return false when a == nil && b != nil", func() {
- a := []string(nil)
- b := []string{}
- So(StringSliceEqual(a, b), ShouldBeFalse)
- })
-
- Convey("should return false when a != nil && b != nil", func() {
- a := []string{"hello", "world"}
- b := []string{"hello", "goconvey"}
- So(StringSliceEqual(a, b), ShouldBeFalse)
- })
- })
- }
这样做的好处是,看单测结果更为清晰直观:
- === RUN TestStringSliceEqual
-
- TestStringSliceEqual
- should return true when a != nil && b != nil ✔
- should return true when a == nil && b == nil ✔
- should return false when a == nil && b != nil ✔
- should return false when a != nil && b != nil ✔
-
-
- 4 total assertions
-
- --- PASS: TestStringSliceEqual (0.00s)
- PASS
- ok infra/alg 0.006s
GWT结构嵌套了三层Convey:最外层是Given层,用来给定测试用例需要的数据;中间一层是When层,用来执行被测试的函数方法,得到result;最内层是Then层,用So来对result进行断言,看结果是否满足期望。
示例代码如下:
- func TestStringSliceEqualIfBothNil(t *testing.T) {
- Convey("Given two string slice which are both nil", t, func() {
- var a []string = nil
- var b []string = nil
- Convey("When the comparision is done", func() {
- result := StringSliceEqual(a, b)
- Convey("Then the result should be true", func() {
- So(result, ShouldBeTrue)
- })
- })
- })
- }
-
- func TestStringSliceNotEqualIfNotBothNil(t *testing.T) {
- Convey("Given two string slice which are both nil", t, func() {
- a := []string(nil)
- b := []string{}
- Convey("When the comparision is done", func() {
- result := StringSliceEqual(a, b)
- Convey("Then the result should be false", func() {
- So(result, ShouldBeFalse)
- })
- })
- })
- }
-
- func TestStringSliceNotEqualIfBothNotNil(t *testing.T) {
- Convey("Given two string slice which are both not nil", t, func() {
- a := []string{"hello", "world"}
- b := []string{"hello", "goconvey"}
- Convey("When the comparision is done", func() {
- result := StringSliceEqual(a, b)
- Convey("Then the result should be false", func() {
- So(result, ShouldBeFalse)
- })
- })
- })
- }
在实际运用中,可以结合第一条方法构成四层嵌套来描述一个测试用例:
- func TestStringSliceEqual(t *testing.T) {
- Convey("TestStringSliceEqualIfBothNotNil", t, func() {
- Convey("Given two string slice which are both not nil", func() {
- a := []string{"hello", "goconvey"}
- b := []string{"hello", "goconvey"}
- Convey("When the comparision is done", func() {
- result := StringSliceEqual(a, b)
- Convey("Then the result should be true", func() {
- So(result, ShouldBeTrue)
- })
- })
- })
- })
-
- Convey("TestStringSliceEqualIfBothNil", t, func() {
- Convey("Given two string slice which are both nil", func() {
- var a []string = nil
- var b []string = nil
- Convey("When the comparision is done", func() {
- result := StringSliceEqual(a, b)
- Convey("Then the result should be true", func() {
- So(result, ShouldBeTrue)
- })
- })
- })
- })
-
- Convey("TestStringSliceNotEqualIfNotBothNil", t, func() {
- Convey("Given two string slice which are both nil", func() {
- a := []string(nil)
- b := []string{}
- Convey("When the comparision is done", func() {
- result := StringSliceEqual(a, b)
- Convey("Then the result should be false", func() {
- So(result, ShouldBeFalse)
- })
- })
- })
- })
-
- Convey("TestStringSliceNotEqualIfBothNotNil", t, func() {
- Convey("Given two string slice which are both not nil", func() {
- a := []string{"hello", "world"}
- b := []string{"hello", "goconvey"}
- Convey("When the comparision is done", func() {
- result := StringSliceEqual(a, b)
- Convey("Then the result should be false", func() {
- So(result, ShouldBeFalse)
- })
- })
- })
- })
-
- }
注意!Given层中最好只有一个Then,因为多个Then会导致每执行完一个Then就会再次执行一遍被测试的函数方法,导致多次执行的结果可能并不相同从而导致意料之外的错误(比如上面示例中的“result := StringSliceEqual(a, b)”)。所以如果选择使用GWT的结构,那么就要保证W中只有一个T,最好也要保证G中只有一个W。
断言函数So中第二个参数Assertion类型定义:
type Assertion func(actual interface{}, expected ...interface{}) string
返回空字符串表示断言成功,否则就是断言失败了。
所以我们自定义断言函数时也要注意这点,下面是一个参考示例:
- 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", t, func() {
- So("summer", ShouldSummerBeComming, "comming")
- So("winter", ShouldSummerBeComming, "comming")
- })
- }
测试结果:
- === RUN TestSummer
-
- TestSummer ✔✘
-
-
- Failures:
-
- * /Users/zhangxiaolong/Desktop/D/go-workspace/src/infra/alg/slice_test.go
- Line 52:
- summer is not comming!
-
-
- 2 total assertions
-
- --- FAIL: TestSummer (0.00s)
- FAIL
- exit status 1
- FAIL infra/alg 0.006s