golang深度测试与灵活比较插件库go-testdeep的使用
Golang深度测试与灵活比较插件库go-testdeep的使用
go-testdeep是一个极其灵活的Golang深度比较库,它扩展了Go的testing包功能。
简介
go-testdeep是一个Golang测试库,它允许进行灵活的数据结构比较,支持从简单到复杂的测试场景。
简单示例
import (
"testing"
"github.com/maxatome/go-testdeep/td"
)
func TestMyFunc(t *testing.T) {
td.Cmp(t, MyFunc(), &Info{Name: "Alice", Age: 42})
}
使用TestDeep运算符的复杂示例
import (
"testing"
"github.com/maxatome/go-testdeep/td"
)
func TestMyFunc(t *testing.T) {
td.Cmp(t, MyFunc(), td.Struct(
&Info{Name: "Alice"},
td.StructFields{
"Age": td.Between(40, 45),
},
))
}
在字面量中使用锚定运算符
import (
"testing"
"github.com/maxatome/go-testdeep/td"
)
func TestMyFunc(tt *testing.T) {
t := td.NewT(tt)
t.Cmp(MyFunc(), &Info{
Name: "Alice",
Age: t.Anchor(td.Between(40, 45)).(int),
})
}
HTTP API测试示例
import (
"testing"
"time"
"github.com/maxatome/go-testdeep/helpers/tdhttp"
"github.com/maxatome/go-testdeep/td"
)
type Person struct {
ID uint64 `json:"id"`
Name string `json:"name"`
Age int `json:"age"`
CreatedAt time.Time `json:"created_at"`
}
func TestMyApi(t *testing.T) {
var id uint64
var createdAt time.Time
testAPI := tdhttp.NewTestAPI(t, myAPI) // ①
testAPI.PostJSON("/person", Person{Name: "Bob", Age: 42}). // ②
Name("Create a new Person").
CmpStatus(http.StatusCreated). // ③
CmpJSONBody(td.JSON(`
// 注意允许注释
{
"id": $id, // 由API/DB设置
"name": "Alice",
"age": Between(40, 45), // ④
"created_at": "$createdAt", // 由API/DB设置
}`,
td.Tag("id", td.Catch(&id, td.NotZero())), // ⑤
td.Tag("createdAt", td.All( // ⑥
td.HasSuffix("Z"), // ⑦
td.Smuggle(func(s string) (time.Time, error) { // ⑧
return time.Parse(time.RFC3339Nano, s)
}, td.Catch(&createdAt, td.Between(testAPI.SentAt(), time.Now()))), // ⑨
)),
))
if !testAPI.Failed() {
t.Logf("The new Person ID is %d and was created at %s", id, createdAt)
}
}
安装
$ go get github.com/maxatome/go-testdeep
辅助工具
tdhttp - HTTP API测试助手
github.com/maxatome/go-testdeep/helpers/tdhttp
包提供了一些函数来轻松测试HTTP处理程序。
tdsuite - 测试套件助手
github.com/maxatome/go-testdeep/helpers/tdsuite
包以非侵入性的方式为go-testdeep添加了测试套件功能。
tdutil - 助手工具
github.com/maxatome/go-testdeep/helpers/tdutil
包允许为go-testdeep助手编写单元测试,并提供了一些有用的函数。
错误输出示例
当测试不匹配时,go-testdeep会生成详细的错误输出,帮助开发者快速定位问题。
许可证
go-testdeep在源代码树的根目录中的LICENSE文件下发布BSD样式的许可证。
更多关于golang深度测试与灵活比较插件库go-testdeep的使用的实战教程也可以访问 https://www.itying.com/category-94-b0.html
更多关于golang深度测试与灵活比较插件库go-testdeep的使用的实战系列教程也可以访问 https://www.itying.com/category-94-b0.html
Go-TestDeep: Golang深度测试与灵活比较插件库
Go-TestDeep 是一个强大的 Golang 测试工具库,它扩展了标准库 testing
的功能,提供了更灵活、更深入的测试比较能力。下面我将详细介绍它的使用方法和优势。
为什么需要 Go-TestDeep
标准库的 testing
包虽然功能完善,但在复杂数据结构的比较上存在一些局限性:
- 深层嵌套结构比较不够直观
- 错误信息不够详细
- 部分比较场景需要手动编写大量代码
Go-TestDeep 解决了这些问题,提供了更强大的断言功能。
安装
go get github.com/maxatome/go-testdeep
基本使用
简单比较
import (
"testing"
td "github.com/maxatome/go-testdeep"
)
func TestSimple(t *testing.T) {
got := 42
td.Cmp(t, got, 42) // 成功
td.Cmp(t, got, 43) // 失败
}
灵活匹配
func TestFlexible(t *testing.T) {
got := []int{1, 2, 3}
// 检查切片是否包含特定元素,顺序不重要
td.Cmp(t, got, td.Bag(3, 1, 2))
// 检查切片是否包含至少一个大于2的元素
td.Cmp(t, got, td.Contains(td.Gt(2)))
}
高级特性
结构体比较
type User struct {
ID int
Name string
CreatedAt time.Time
}
func TestStruct(t *testing.T) {
user := User{
ID: 1,
Name: "Alice",
CreatedAt: time.Now(),
}
// 忽略时间字段的比较
td.Cmp(t, user, td.Struct(
User{
ID: 1,
Name: "Alice",
},
td.StructFields{
"CreatedAt": td.Ignore(),
},
))
}
JSON 比较
func TestJSON(t *testing.T) {
got := `{"name":"Bob","age":30,"hobbies":["reading","swimming"]}`
td.CmpJSON(t, got, `{
"name": "Bob",
"age": td.Between(30, 40),
"hobbies": td.ArrayEach(td.String()),
}`)
}
自定义匹配器
func TestCustom(t *testing.T) {
got := "Hello, 世界"
// 自定义匹配器:检查字符串是否是有效的UTF-8
td.Cmp(t, got, td.Code(func(s string) bool {
return utf8.ValidString(s)
}))
}
常用操作符
Go-TestDeep 提供了丰富的操作符:
-
基本比较:
td.Eq(val)
: 严格等于td.Not(val)
: 不等于td.Gt(val)
,td.Gte(val)
,td.Lt(val)
,td.Lte(val)
: 大于/小于比较
-
集合操作:
td.Bag(items...)
: 无序集合匹配td.Set(items...)
: 无序集合匹配,不考虑重复td.SubSetOf(items...)
: 子集匹配td.SuperSetOf(items...)
: 超集匹配
-
类型检查:
td.Isa(typ)
: 检查类型td.Struct(model, fields)
: 结构体匹配
-
特殊匹配:
td.Re(pattern)
: 正则匹配td.Smuggle(fn, expected)
: 转换后匹配td.Lax(expected)
: 宽松类型匹配
最佳实践
-
清晰的错误信息:
td.Cmp(t, getUser(), td.Struct( User{Name: "Alice"}, ), "check user details")
-
组合匹配器:
td.Cmp(t, getResult(), td.All( td.Struct(Result{Status: "success"}), td.Smuggle(func(r Result) int { return len(r.Items) }, td.Gte(1)), ))
-
表格驱动测试:
func TestAdd(t *testing.T) { cases := []struct { a, b int expected interface{} }{ {1, 2, 3}, {0, 0, 0}, {-1, 1, td.Between(0, 1)}, } for _, tc := range cases { td.Cmp(t, Add(tc.a, tc.b), tc.expected) } }
性能考虑
Go-TestDeep 虽然功能强大,但在性能敏感的场景下需要注意:
- 复杂匹配器会比简单比较消耗更多资源
- 在热路径测试中考虑使用标准库的简单比较
- 可以结合
testing.B
进行基准测试
总结
Go-TestDeep 为 Golang 测试提供了:
- 更丰富的比较操作
- 更清晰的错误信息
- 更灵活的匹配方式
- 更好的可维护性
对于复杂数据结构的测试,它能显著减少样板代码,提高测试的可读性和可维护性。对于简单的单元测试,标准库可能更合适,但对于集成测试和复杂场景,Go-TestDeep 是一个强大的工具。