frankban/quicktest

GitHub: frankban/quicktest

quicktest 是一个 Go 语言测试辅助库,提供丰富的断言 Checker 和测试资源管理工具,帮助开发者更简洁高效地编写测试代码。

Stars: 528 | Forks: 27

[![Go 参考](https://pkg.go.dev/badge/github.com/frankban/quicktest.svg)](https://pkg.go.dev/github.com/frankban/quicktest#section-documentation) [![构建状态](https://github.com/frankban/quicktest/actions/workflows/ci.yaml/badge.svg)](https://github.com/frankban/quicktest/actions/workflows/ci.yaml) ### quicktest `go get github.com/frankban/quicktest@latest` quicktest 包提供了一组用于编写测试的 Go 辅助工具。 Quicktest 辅助工具可以轻松集成到常规的 Go 测试中,例如: ``` import qt "github.com/frankban/quicktest" func TestFoo(t *testing.T) { t.Run("numbers", func(t *testing.T) { c := qt.New(t) numbers, err := somepackage.Numbers() c.Assert(err, qt.IsNil) c.Assert(numbers, qt.DeepEquals, []int{42, 47}) }) t.Run("bad wolf error", func(t *testing.T) { c := qt.New(t) numbers, err := somepackage.Numbers() c.Assert(err, qt.ErrorMatches, "bad wolf") }) t.Run("nil", func(t *testing.T) { c := qt.New(t) got := somepackage.MaybeNil() c.Assert(got, qt.IsNil, qt.Commentf("value: %v", somepackage.Value)) }) } ``` ### 断言 断言看起来像这样,其中 qt.Equals 可以替换为任何可用的 checker。如果断言失败,将调用底层的 Fatal 方法来描述错误并中止测试。 ``` c := qt.New(t) c.Assert(someValue, qt.Equals, wantValue) ``` 如果你不想在失败时中止,请改用 Check,它会调用 Error 而不是 Fatal: ``` c.Check(someValue, qt.Equals, wantValue) ``` 对于非常简短的测试,可以省略实例化 *qt.C 的额外行: ``` qt.Assert(t, someValue, qt.Equals, wantValue) qt.Check(t, someValue, qt.Equals, wantValue) ``` 该库提供了一些基础的 checker,例如 Equals、DeepEquals、Matches、ErrorMatches、IsNil 等。可以通过实现 Checker 接口添加更多 checker。下面,我们按字母顺序列出该包实现的 checker。 ### All All 返回一个 Checker,它使用给定的 checker 来检查 slice 或 array 的元素,或 map 的值。如果所有元素都通过检查,它就会成功。如果失败,它会打印第一个失败索引的错误。 例如: ``` c.Assert([]int{3, 5, 8}, qt.All(qt.Not(qt.Equals)), 0) c.Assert([][]string{{"a", "b"}, {"a", "b"}}, qt.All(qt.DeepEquals), []string{"c", "d"}) ``` 另请参见 Any 和 Contains。 ### Any Any 返回一个 Checker,它使用给定的 checker 来检查 slice 或 array 的元素,或 map 的值。如果任何元素通过检查,它就会成功。 例如: ``` c.Assert([]int{3,5,7,99}, qt.Any(qt.Equals), 7) c.Assert([][]string{{"a", "b"}, {"c", "d"}}, qt.Any(qt.DeepEquals), []string{"c", "d"}) ``` 另请参见 All 和 Contains。 ### CmpEquals CmpEquals 根据提供的比较选项检查两个任意值的相等性。当不需要比较选项时,通常使用 DeepEquals。 调用示例: ``` c.Assert(list, qt.CmpEquals(cmpopts.SortSlices), []int{42, 47}) c.Assert(got, qt.CmpEquals(), []int{42, 47}) // Same as qt.DeepEquals. ``` ### CodecEquals CodecEquals 返回一个检查编解码器值是否等价的 checker。 ``` func CodecEquals( marshal func(interface{}) ([]byte, error), unmarshal func([]byte, interface{}) error, opts ...cmp.Option, ) Checker ``` 它期望两个参数:一个包含由编解码器序列化的数据的字节 slice 或字符串,以及一个 Go 值。 它使用 unmarshal 将数据反序列化到一个 interface{} 值中。它使用 marshal 序列化该 Go 值,然后将结果反序列化到一个 interface{} 值中。 然后它检查这两个 interface{} 值是否彼此深度相等,并使用 CmpEquals(opts) 来执行检查。 有关其用法的示例,请参见 JSONEquals。 ### Contains Contains 检查 map、slice、array 或 string 是否包含某个值。它与使用 Any(Equals) 相同,只是它对字符串有一个特殊情况——如果第一个参数是字符串,则第二个参数也必须是字符串,并且将使用 strings.Contains。 例如: ``` c.Assert("hello world", qt.Contains, "world") c.Assert([]int{3,5,7,99}, qt.Contains, 7) ``` ### ContentEquals ContentEquals 类似于 DeepEquals,但在比较值之前,会对比较值中的所有 slice 进行排序。 例如: ``` c.Assert([]string{"c", "a", "b"}, qt.ContentEquals, []string{"a", "b", "c"}) ``` ### DeepEquals DeepEquals 检查两个任意值是否深度相等。比较是使用 github.com/google/go-cmp/cmp 包完成的。比较 struct 时,默认情况下不允许导出的字段。如果需要更复杂的比较,请使用 CmpEquals(见下文)。 调用示例: ``` c.Assert(got, qt.DeepEquals, []int{42, 47}) ``` ### Equals Equals 检查两个值是否相等,就像使用 Go 的 == 运算符进行比较一样。 例如: ``` c.Assert(answer, qt.Equals, 42) ``` 请注意,以下操作将会失败: ``` c.Assert((*sometype)(nil), qt.Equals, nil) ``` 对于这种 nil 检查,请使用下方的 IsNil checker。 ### ErrorAs ErrorAs 检查错误是否为特定的 error 类型或包装了该类型。如果是,则将其分配给提供的指针。这类似于调用 errors.As。 例如: ``` // Checking for a specific error type c.Assert(err, qt.ErrorAs, new(*os.PathError)) // Checking fields on a specific error type var pathError *os.PathError if c.Check(err, qt.ErrorAs, &pathError) { c.Assert(pathError.Path, Equals, "some_path") } ``` ### ErrorIs ErrorIs 检查错误是否为特定的 error 值或包装了该值。这类似于调用 errors.Is。 例如: ``` c.Assert(err, qt.ErrorIs, os.ErrNotExist) ``` ### ErrorMatches ErrorMatches 检查提供的值是否为一个 error,且其消息与提供的正则表达式匹配。 例如: ``` c.Assert(err, qt.ErrorMatches, `bad wolf .*`) ``` ### HasLen HasLen 检查提供的值是否具有给定的长度。 例如: ``` c.Assert([]int{42, 47}, qt.HasLen, 2) c.Assert(myMap, qt.HasLen, 42) ``` ### Implements Implements 检查提供的值是否实现了某个接口。该接口由指向接口变量的指针指定。 例如: ``` var rc io.ReadCloser c.Assert(myReader, qt.Implements, &rc) ``` ### IsFalse IsFalse 检查提供的值是否为 false。该值必须具有布尔底层类型。 例如: ``` c.Assert(false, qt.IsFalse) c.Assert(IsValid(), qt.IsFalse) ``` ### IsNil IsNil 检查提供的值是否为 nil。 例如: ``` c.Assert(got, qt.IsNil) ``` 作为一种特殊情况,如果该值为 nil 但实现了 error 接口,它仍然被认为是非 nil 的。这意味着如果某个 error 值的底层值恰好为 nil,IsNil 将会失败,因为这通常是一个错误。参见 https://golang.org/doc/faq#nil_error。 因此,像这样检查 error 是完全没问题的: ``` c.Assert(err, qt.IsNil) ``` ### IsNotNil IsNotNil 是一个检查所提供的值不为 nil 的 Checker。IsNotNil 等同于 qt.Not(qt.IsNil) 例如: ``` c.Assert(got, qt.IsNotNil) ``` ### IsTrue IsTrue 检查提供的值是否为 true。该值必须具有布尔底层类型。 例如: ``` c.Assert(true, qt.IsTrue) c.Assert(myBoolean(false), qt.IsTrue) ``` ### JSONEquals JSONEquals 检查字节 slice 或字符串是否与 Go 值在 JSON 上等价。有关更多信息,请参见 CodecEquals。 它使用 DeepEquals 进行比较。如果需要更复杂的比较,请直接使用 CodecEquals。 例如: ``` c.Assert(`{"First": 47.11}`, qt.JSONEquals, &MyStruct{First: 47.11}) ``` ### Matches Matches 检查字符串或调用 String 方法的结果(如果该值实现了 fmt.Stringer)是否与提供的正则表达式匹配。 例如: ``` c.Assert("these are the voyages", qt.Matches, `these are .*`) c.Assert(net.ParseIP("1.2.3.4"), qt.Matches, `1.*`) ``` ### Not Not 返回一个对给定 Checker 取反的 Checker。 例如: ``` c.Assert(got, qt.Not(qt.IsNil)) c.Assert(answer, qt.Not(qt.Equals), 42) ``` ### PanicMatches PanicMatches 检查提供的函数是否发生 panic,且其消息与提供的正则表达式匹配。 例如: ``` c.Assert(func() {panic("bad wolf ...")}, qt.PanicMatches, `bad wolf .*`) ``` ### Satisfies Satisfies 检查所提供的值作为所提供的断言函数的参数使用时,是否会导致该函数返回 true。该函数的类型必须为 func(T) bool,且该值可分配给 T。 例如: ``` // Check that an error from os.Open satisfies os.IsNotExist. c.Assert(err, qt.Satisfies, os.IsNotExist) // Check that a floating point number is a not-a-number. c.Assert(f, qt.Satisfies, math.IsNaN) ``` ### 延迟执行 testing.TB.Cleanup 辅助工具提供了延迟执行函数的能力,这些函数将在测试完成时运行。这对于创建操作系统级别的资源(例如临时目录,参见 c.Mkdir)通常非常有用。 当目标 Go 版本不具备 Cleanup 功能(< 1.14)时,可以使用 c.Defer 实现相同的效果。在这种情况下,需要调用 c.Done 来触发延迟行为。例如,如果你在顶层创建了一个 *C 实例,则必须添加一个 defer 以便在测试结束时触发清理: ``` defer c.Done() ``` 但是,如果你使用 quicktest 创建子测试,Done 将在该子测试结束时自动调用。例如: ``` func TestFoo(t *testing.T) { c := qt.New(t) c.Run("subtest", func(c *qt.C) { c.Setenv("HOME", c.Mkdir()) // Here $HOME is set the path to a newly created directory. // At the end of the test the directory will be removed // and HOME set back to its original value. }) } ``` c.Patch、c.Setenv、c.Unsetenv 和 c.Mkdir 辅助工具在可用时会使用 t.Cleanup 清理资源,否则会回退到 Defer。 有关完整的 API 参考,请参见[包文档](https://pkg.go.dev/github.com/frankban/quicktest#section-documentation)。
标签:EVTX分析, Go, Ruby工具, SOC Prime, 单元测试, 开发工具, 断言库, 日志审计, 测试框架