本文是李文周的博客go语言学习之路的目录提要版,方便复习查看使用,原文参见文章连接:
https://www.liwenzhou.com/posts/Go/go_menu/
不写测试的开发不是好程序员。我个人非常崇尚TDD(Test Driven Development)的,然而可惜的是国内的程序员都不太关注测试这一部分。 这篇文章主要介绍下在Go语言中如何做单元测试和基准测试。
Go语言中的测试依赖go test
命令。编写测试代码和编写普通的Go代码过程是类似的,并不需要学习新的语法、规则或工具。
go test命令是一个按照一定约定和组织的测试代码的驱动程序。在包目录内,所有以_test.go
为后缀名的源代码文件都是go test
测试的一部分,不会被go build
编译到最终的可执行文件中。
在*_test.go
文件中有三种类型的函数,单元测试函数、基准测试函数和示例函数。
类型 | 格式 | 作用 |
---|---|---|
测试函数 | 函数名前缀为Test | 测试程序的一些逻辑行为是否正确 |
基准函数 | 函数名前缀为Benchmark | 测试函数的性能 |
示例函数 | 函数名前缀为Example | 为文档提供示例文档 |
go test
命令会遍历所有的*_test.go
文件中符合上述命名规则的函数,然后生成一个临时的main包用于调用相应的测试函数,然后构建并运行、报告测试结果,最后清理测试中生成的临时文件。
每个测试函数必须导入testing
包,测试函数的基本格式(签名)如下:
func TestName(t *testing.T){
// ...
}
测试函数的名字必须以Test
开头,可选的后缀名必须以大写字母开头,举几个例子:
func TestAdd(t *testing.T){
... }
func TestSum(t *testing.T){
... }
func TestLog(t *testing.T){
... }
其中参数t
用于报告测试失败和附加的日志信息。 testing.T
的拥有的方法如下:
func (c *T) Error(args ...interface{
})
func (c *T) Errorf(format string, args ...interface{
})
func (c *T) Fail()
func (c *T) FailNow()
func (c *T) Failed() bool
func (c *T) Fatal(args ...interface{
})
func (c *T) Fatalf(format string, args ...interface{
})
func (c *T) Log(args ...interface{
})
func (c *T) Logf(format string, args ...interface{
})
func (c *T) Name() string
func (t *T) Parallel()
func (t *T) Run(name string, f func(t *T)) bool
func (c *T) Skip(args ...interface{
})
func (c *T) SkipNow()
func (c *T) Skipf(format string, args ...interface{
})
func (c *T) Skipped() bool
接下来,我们定义一个split
的包,包中定义了一个Split
函数,具体实现如下:
// split/split.go
package split
import "strings"
// split package with a single split function.
// Split slices s into all substrings separated by sep and
// returns a slice of the substrings between those separators.
func Split(s, sep string) (result []string) {
i := strings.Index(s, sep)
for i > -1 {
result = append(result, s[:i])
s = s[i+1:]
i = strings.Index(s, sep)
}
result = append(result, s)
return
}
在当前目录下,我们创建一个split_test.go
的测试文件,并定义一个测试函数如下:
// split/split_test.go
package split
import (
"reflect"
"testing"
)
func TestSplit(t *testing.T) {
// 测试函数名必须以Test开头,必须接收一个*testing.T类型参数
got := Split("a:b:c", ":") // 程序输出的结果
want := []string{
"a", "b", "c"} // 期望的结果
if !reflect.DeepEqual(want, got) {
// 因为slice不能比较直接,借助反射包中的方法比较
t.Errorf("excepted:%v, got:%v", want, got) // 测试失败输出错误提示
}
}
在split
包路径下,执行go test
命令,可以看到输出结果如下:
split $ go test
PASS
ok github.com/Q1mi/studygo/code_demo/test_demo/split 0.005s
我们现在还想要测试一下split
函数对中文字符串的支持,这个时候我们可以再编写一个TestChineseSplit
测试函数,但是我们也可以使用如下更友好的一种方式来添加更多的测试用例。
func TestSplit(t *testing.T) {
// 定义一个测试用例类型
type test struct {
input string
sep string
want [