ThinkChat🤖让你学习和工作更高效,注册即送10W Token,即刻开启你的AI之旅 广告
[TOC] ## <span style="font-size:15px">**一、安装**</span> ``` go get github.com/smartystreets/goconvey ``` 官方文档: https://github.com/smartystreets/goconvey/wiki/Documentation https://pkg.go.dev/github.com/smartystreets/goconvey@v1.8.1/convey#pkg-overview ## <span style="font-size:15px">**二、库函数说明**</span> | 库函数 | 说明 | | --- | --- | | Convey(items ...interface{}) | Convey是用于声明的作用域时使用的方法,每个作用域都有一个描述和一个func(),其中可能包含对Convey()、Reset()或Should样式断言的其他调用。 | | FocusConvey(items ...any) | FocusConvey 函数用于标记一个测试函数,使其成为焦点测试。当运行测试套件时,只有被标记为焦点的测试函数会被运行。 | | SkipConvey(items ...interface{}) |对应的闭包函数将不被执行。 | | So(actual interface{}, assert Assertion, expected ...interface{}) | 在测试中进行断言。它的作用是比较实际值(actual)和期望值(expected),并根据断言(assert)的结果来判断测试是否通过。 | | SoMsg(msg string, actual interface{}, assert Assertion, expected ...interface{}) | 用于在测试中进行断言。它的作用是比较实际值和期望值,并根据断言结果输出相应的测试信息(msg) | | SkipSo(stuff ...interface{}) | 用于在测试中跳过特定的断言。当测试中的某些条件不满足时,可以使用 SkipSo 函数来跳过对应的断言,使得测试继续执行而不会因为特定条件的失败而中断。这样可以让测试在某些情况下继续执行,同时记录下跳过的断言,方便后续分析和调试。 | | Reset(action func()) | Reset注册一个清理函数,该函数将在同一作用域中的每个Convey()之后运行 | | Println(items ...interface{}) (int, error) | 打印日志,类似于fmt | | Print(items ...interface{}) (int, error) | 打印日志,类似于fmt | | Printf(format string, items ...interface{}) (int, error) | 打印日志,类似于fmt | | SetDefaultFailureMode(mode FailureMode) | 用于设置默认的测试失败模式。参数 mode 是一个枚举类型 FailureMode,用于表示测试失败时的行为,包括 FailureHalts(默认行为,测试失败会导致测试停止)和 FailureContinues(测试失败不会停止,继续执行其他测试) | | SetDefaultStackMode(mode StackMode) | 用于设置默认的堆栈模式。该函数接受一个 StackMode 类型的参数,并将默认的堆栈模式设置为指定的模式。 | | SuppressConsoleStatistics() | SuppressConsoleStatistics阻止控制台统计信息的自动打印。显式调用PrintConsoleStatistics将强制打印统计数据。 | | PrintConsoleStatistics() | 打印断言统计数据 | ## <span style="font-size:15px">**三、标准断言**</span> ### <span style="font-size:15px">**1、通用相等比较**</span> ``` So(thing1, ShouldEqual, thing2) // 相等 So(thing1, ShouldNotEqual, thing2) // 不相等 ``` ### <span style="font-size:15px">**2、深度比较:数组、切片、map和结构体**</span> ``` So(thing1, ShouldResemble, thing2) // 断言 thing1 应该与 thing2 相似。相似性的判断是通过比较两个对象的值来确定的 So(thing1, ShouldNotResemble, thing2) // 断言 thing1 与 thing2 不相似。 So(thing1, ShouldPointTo, thing2) // 断言 thing1 应该指向 thing2。这通常用于检查指针是否指向了正确的对象。 So(thing1, ShouldNotPointTo, thing2) // 断言 thing1 不应该指向 thing2。 So(thing1, ShouldBeNil) // 断言 thing1 应该为 nil,即空值。 So(thing1, ShouldNotBeNil) // 断言 thing1 不应该为 nil。 So(thing1, ShouldBeTrue) // 断言 thing1 应该为True。 So(thing1, ShouldBeFalse) // 断言 thing1 应该为False So(thing1, ShouldBeZeroValue) // 断言 thing1 应该为其类型的零值。这通常用于检查变量是否被正确初始化为零值。 ``` ### <span style="font-size:15px">**3、数值比较**</span> ``` So(1, ShouldBeGreaterThan, 0) // 断言验证1是否大于0。 So(1, ShouldBeGreaterThanOrEqualTo, 0) // 断言验证1是否大于或等于0。 So(1, ShouldBeLessThan, 2) // 断言验证1是否小于2。 So(1, ShouldBeLessThanOrEqualTo, 2) // 断言验证1是否小于或等于2。 So(1.1, ShouldBeBetween, .8, 1.2) // 断言验证1.1是否在0.8和1.2之间。 So(1.1, ShouldNotBeBetween, 2, 3) // 断言验证1.1是否不在2和3之间。 So(1.1, ShouldBeBetweenOrEqual, .9, 1.1) // 断言验证1.1是否在0.9和1.1之间,包括边界值。 So(1.1, ShouldNotBeBetweenOrEqual, 1000, 2000) // 断言验证1.1是否不在1000和2000之间,包括边界值。 So(1.0, ShouldAlmostEqual, 0.99999999, .0001) // 断言验证1.0是否近似等于0.99999999,允许的误差为0.0001。 So(1.0, ShouldNotAlmostEqual, 0.9, .0001) // 断言验证1.0是否不近似等于0.9,允许的误差为0.0001。 ``` ### <span style="font-size:15px">**4、数据集合比较**</span> ``` So([]int{2, 4, 6}, ShouldContain, 4) // 断言切片[]int{2, 4, 6}中包含元素4。 So([]int{2, 4, 6}, ShouldNotContain, 5) // 断言切片[]int{2, 4, 6}中不包含元素5。 So(4, ShouldBeIn, ...[]int{2, 4, 6}) // 断言元素4存在于切片[]int{2, 4, 6}中。 So(4, ShouldNotBeIn, ...[]int{1, 3, 5}) // 断言元素4不存在于切片[]int{1, 3, 5}中。 So([]int{}, ShouldBeEmpty) // 断言空切片 So([]int{1}, ShouldNotBeEmpty) // 断言切片不为空 So(map[string]string{"a": "b"}, ShouldContainKey, "a") // 断言map中包含键a So(map[string]string{"a": "b"}, ShouldNotContainKey, "b") // 断言map中不包含键b So(map[string]string{"a": "b"}, ShouldNotBeEmpty) // 断言非空map So(map[string]string{}, ShouldBeEmpty) // 断言空map So(map[string]string{"a": "b"}, ShouldHaveLength, 1) // 断言map的长度为1 ``` ### <span style="font-size:15px">**5、字符串比较**</span> ``` So("asdf", ShouldStartWith, "as") // 断言"asdf"应该以"as"开头 So("asdf", ShouldNotStartWith, "df") // 断言"asdf"不应该以"df"开头 So("asdf", ShouldEndWith, "df") // 断言"asdf"应该以"df"结尾 So("asdf", ShouldNotEndWith, "df") // 断言"asdf"不应该以"df"结尾 So("asdf", ShouldContainSubstring, "sd") // 断言"asdf"应该包含子串"sd" So("asdf", ShouldNotContainSubstring, "er") // 断言"asdf"不应该包含子串"er" So("adsf", ShouldBeBlank) // 断言"adsf"应该为空白字符串 So("asdf", ShouldNotBeBlank) // 断言"asdf"不应该是空白字符串 ``` ### <span style="font-size:15px">**6、异常比较**</span> ``` So(func(), ShouldPanic) // 用于测试一个函数是否会触发panic,触发则通过 So(func(), ShouldNotPanic) // 用于测试一个函数是否不会触发panic,不触发则通过 So(func(), ShouldPanicWith, "") // 用于测试一个函数触发恐慌时的恐慌信息。如果 func() 执行时触发了恐慌,并且恐慌信息与提供的字符串匹配,这个断言会通过测试;否则测试将失败。也可以使用 errors.New("something") 作为期望的恐慌信息 So(func(), ShouldNotPanicWith, "") // 用于测试一个函数触发恐慌时的恐慌信息。如果 func() 执行时没有触发恐慌,或者触发了恐慌但恐慌信息与提供的字符串不匹配,这个断言会通过测试;如果触发了恐慌且恐慌信息与提供的字符串匹配,测试将失败。也可以使用 errors.New("something") 作为不期望的恐慌信息。 ``` ### <span style="font-size:15px">**7、类型检查**</span> ``` So(1, ShouldHaveSameTypeAs, 0) // 检查 1 的类型是否与 0 的类型相同 So(1, ShouldNotHaveSameTypeAs, "asdf") // 检查 1 的类型是否与字符串 "asdf" 的类型不同 ``` ### <span style="font-size:15px">**8、时间比较**</span> ``` So(time.Now(), ShouldHappenBefore, time.Now()) // 断言第一个时间应该在第二个时间之前 So(time.Now(), ShouldHappenOnOrBefore, time.Now()) // 断言第一个时间应该等于或在第二个时间之前 So(time.Now(), ShouldHappenAfter, time.Now()) // 断言第一个时间应该在第二个时间之后 So(time.Now(), ShouldHappenOnOrAfter, time.Now()) // 断言第一个时间应该等于或在第二个时间之后 So(time.Now(), ShouldHappenBetween, time.Now(), time.Now()) // 断言第一个时间应该在第二个和第三个时间之间 So(time.Now(), ShouldHappenOnOrBetween, time.Now(), time.Now()) // 断言第一个时间应该等于或在第二个和第三个时间之间 So(time.Now(), ShouldNotHappenOnOrBetween, time.Now(), time.Now()) // 断言第一个时间不应该等于或在第二个和第三个时间之间 So(time.Now(), ShouldHappenWithin, duration, time.Now()) // 断言第一个时间应该在第二个时间之前指定的持续时间范围内 So(time.Now(), ShouldNotHappenWithin, duration, time.Now()) // 断言第一个时间不应该在第二个时间之前指定的持续时间范围内 ``` ## <span style="font-size:15px">**四、自定义断言**</span> 框架支持自定义函数进行断言内容的封装,然后在So()函数中进行调用 ``` func shouldScareGophersMoreThan(actual interface{}, expected ...interface{}) string { if actual == "BOO!" && expected[0] == "boo" { return "" } return "assert failed" } func TestSummer(t *testing.T) { Convey("custom assert", func() { So("BOO!", shouldScareGophersMoreThan, "boo") }) } ``` ## <span style="font-size:15px">**五、用例编写**</span> &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;单元测试的名称需以 Test 开头,并需要接收一个类型为 \*testing.T 的参数。 &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;每个测试用例需要使用Convey函数包裹起来,第一个参数为string类型的测试用例描述;第二个参数一般为 \*testing.T;第三个参数为不接收任何参数也不返回任何值的函数(通常以闭包的形式书写)。Convey语句可以无限嵌套,以体现各个测试用例之间的关系,只有最外层的 Convey 需要传入\*testing.T类型变量,内层嵌套的Convey不需要传入。 &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;当存在SkipConvey或SkipSo时,测试日志中会显式打上"skipped"形式的标记: &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;当测试代码中存在SkipConvey时,相应闭包函数中不管是否为SkipSo,都将被忽略,测试日志中对应的符号仅为一个"⚠" &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;当测试代码Convey语句中存在SkipSo时,测试日志中每个So对应一个"✔"或"✘",每个SkipSo对应一个"⚠",按实际顺序排列 &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;不管存在SkipConvey还是SkipSo时,测试日志中都有字符串"{n} total assertions (one or more sections skipped)",其中{n}表示测试中实际已运行的断言语句数。 ``` package operate import ( . "github.com/smartystreets/goconvey/convey" "testing" ) func Add(a, b int) int { return a + b } func Subtract(a, b int) int { return a - b } func TestAdd(t *testing.T) { Convey("将两数相加", t, func() { So(Add(1, 2), ShouldEqual, 3) }) } func TestOperate(t *testing.T) { Convey("数值操作", t, func() { Convey("将两数相加", func() { So(Add(1, 2), ShouldEqual, 3) }) SkipConvey("将两数相减", func() { // 跳过测试 So(Subtract(2, 1), ShouldEqual, 1) }) }) } func TestFocusOperate(t *testing.T) { Convey("将两数相减", t, func() { So(Subtract(2, 1), ShouldEqual, 1) }) FocusConvey("聚焦操作", t, func() { Convey("此用例不会执行", func() { So(Add(1, 2), ShouldEqual, 3) }) FocusConvey("只执行这用例", func() { So(Add(2, 2), ShouldEqual, 4) }) }) } ``` 运行结果: ``` === RUN TestAdd 将两数相加 ✔ 1 total assertion --- PASS: TestAdd (0.00s) === RUN TestOperate 数值操作 将两数相加 ✔ 将两数相减 ⚠ 2 total assertions (one or more sections skipped) --- PASS: TestOperate (0.00s) === RUN TestFocusOperate 将两数相减 ✔ 3 total assertions (one or more sections skipped) 聚焦操作 只执行这用例 ✔ 4 total assertions (one or more sections skipped) --- PASS: TestFocusOperate (0.00s) PASS ```