📌Golang📌基础📌T-testing测试.txt
Golang中可以使用标准库"testing"包编写单元测试和基准测试,使用`go test`命令执行单元测试和基准测试的代码。

测试文件名以_test.go结尾,文件名开头一般是被测试函数所在的文件名。
test工具可以遍历以_test.go结尾的文件,执行测试函数,而build和run命令会忽略以_test.go结尾的文件。

在编写单元测试代码时,一般会得到一个实际输出结果,和一个预期的输出结果做对比。
针对这两个变量,社区的变量名规范是got/want或expected/actual。

单元测试的函数名以Test开头,参数必须是*testing.T类型。
    func TestXxx(t *testing.T) {}

基准测试的函数名以Benchmark开头,参数必须是*testing.B类型,通常在函数体中for循环的条件,以b.N作为循环次数。
基准函数会运行目标代码b.N次,在执行期间,会调整b.N直到基准测试函数持续足够长的时间。
    func BenchmarkXxx(b *testing.B) {
        // some initialization
    	for i := 0; i < b.N; i++ {
    		// ......
    	}
    }

example声明的命名约定:包,函数F,类型T,类型T上的方法M依次是:
    func Example() { ... }
    func ExampleF() { ... }
    func ExampleT() { ... }
    func ExampleT_M() { ... }
示例函数可以包括以"Output:"开头的行注释,并在运行测试时与函数的标准输出进行比较,比较时会忽略前导和尾随空格。
    func ExampleHello() {
            fmt.Println("hello")
            // Output: hello
    }
    func ExampleSalutations() {
            fmt.Println("hello, and")
            fmt.Println("goodbye")
            // Output:
            // hello, and
            // goodbye
    }
"Unordered output:"形式的注释,和"Output:" 类似,但是能够以任意顺序匹配行:
    func ExamplePerm() {
        for _, value := range Perm(4) {
            fmt.Println(value)
        }
        // Unordered output: 4
        // 2
        // 1
        // 3
        // 0
    }
没有输出注释的示例函数被编译但不执行。

模糊测试的函数名以Fuzz开头,参数必须是*testing.F,Fuzz方法接受一个函数,第一个参数为*testing.T,第二个参数类型为Add方法参数的类型。
    func FuzzHex(f *testing.F) {
        for _, seed := range [][]byte{{}, {0}, {9}, {0xa}, {0xf}, {1, 2, 3, 4}} {
            f.Add(seed)
        }
        f.Fuzz(func(t *testing.T, in []byte) {
            enc := hex.EncodeToString(in)
            out, err := hex.DecodeString(enc)
            if err != nil {
                t.Fatalf("%v: decode: %v", in, err)
            }
            if !bytes.Equal(in, out) {
                t.Fatalf("%v: not equal after round trip: %v", in, out)
            }
        })
    }

如果有需要,可以调用*T和*B的Skip方法,跳过该测试或基准测试:
    if testing.Short() {
        t.Skip("skipping test in short mode.")
    }

T和B的Run方法允许定义子单元测试和子基准测试,而不必为每个子测试和子基准定义单独的函数。
子测试也可用于控制并行性。所有的子测试完成后,父测试才会完成。
    t.Run("A=1", func(t *testing.T) {
        t.Parallel()
        // ......
    })
    t.Run("B=2", func(t *testing.T) {
        t.Parallel()
        // ......
    })

如果基准测试需要在并行设置中测试性能,则可以使用RunParallel辅助函数; 这样的基准测试一般与-cpu标志一起使用。
    b.RunParallel(func(pb *testing.PB) {
        for pb.Next() {
            // ......
        }
    })

如果测试文件包含函数: func TestMain(m *testing.M)
那么生成的测试将调用TestMain(m),而不是直接运行测试。
TestMain运行在主goroutine中, 可以在调用m.Run前后做任何设置和拆卸。
应该使用m.Run的返回值作为参数调用os.Exit。
    func TestMain(m *testing.M) {
        //如果TestMain使用了flags,这里应该加上flag.Parse()
        os.Exit(m.Run())
    }

命令参数 go test [build/test flags] [packages] [build/test flags & test binary flags]
go test #运行当前目录下的单元测试
go test project/ab #运行project项目(mod)下ab包的单元测试
go test ./... #运行当前目录及所有子目录下的测试用例

-cover 开启测试覆盖率
-v 参数可以打印详细的日志。默认只打印简单的测试结果。
-json 参数将结果输出转换成json格式,以方便自动化测试解析使用。每条json的key有:
Time(string),Action(string),Package(string),Test(string),还可能有Elapsed(float),Output(string)

-run regexp 跟据正则表达式执行单元测试和示例测试。
-bench regexp 默认不执行性能测试,使用-bench参数才可以运行,而且只运行性能测试函数。
如果要执行所有的性能测试,使用参数"-bench ."或"-bench=."

-run和-bench命令行标志的参数是与测试名称相匹配的非固定的正则表达式。
go test -run Foo      #匹配"Foo"的顶层测试,例如"TestFooBar"。
go test -run Foo/A=   #匹配顶层测试"Foo",运行其匹配"A="的子测试。
go test -run /A=1     #运行所有匹配"A=1"的子测试。
go test -fuzz FuzzFoo #运行模糊测试FuzzFoo

-cpu 参数提供一个CPU个数的列表,提供此列表后,那么测试将按照这个列表指定的CPU数设置GOMAXPROCS并分别测试。
比如"-cpu 1,2",那么每个测试将执行两次,一次是用1个CPU执行,一次是用2个CPU执行。

-benchtime <t>s 指定每个性能测试的执行时间,如果不指定,则使用默认时间1s。
-count n 指定每个测试执行的次数,默认执行一次。
-parallel n 指定测试的最大并发数。
-benchmem 打印每个操作分配的字节数、每个操作分配的对象数。

benchmark结果示例解析:
BenchmarkFoo-8		5051356		240.8 ns/op		144 B/op		4 allocs/op
函数名-线程数GOMAXPROCS
执行次数b.N
ns/op:每op耗时纳秒数
B/op:每op申请内存字节数
allocs/op:每op申请内存次数

========== ========== 类型T和B和F常用方法 ========== ==========

Log(args ...any) Logf(format string, args ...any)
使用与Printf相同的格式化语法对它的参数进行格式化,然后将格式化后的文本记录到错误日志里面。
Fail() 将当前测试标识为失败,但是仍继续执行该测试。
FailNow() 将当前测试标识为失败并停止执行该测试,在此之后,测试过程将在下一个测试或者下一个基准测试中继续。
Error(args ...any) 相当于在调用Log之后调用Fail。
Errorf(format string, args ...any) 相当于在调用Logf之后调用Fail。
Fatal(args ...any) 相当于在调用Log之后调用FailNow。
Fatalf(format string, args ...any) 相当于在调用Logf之后调用FailNow。
SkipNow() 将当前测试标识为“被跳过”并停止执行该测试。
如果一个测试在失败(Error、Errorf和Fail)之后被跳过了,那么它还是会被判断为是“失败的”。
Skip(args ...any) 相当于在调用Log之后调用SkipNow。
Skipf(format string, args ...any) 相当于在调用Logf之后调用SkipNow。

func (t *T) Run(name string, f func(t *T)) bool
执行名字为name的子测试f ,并报告f在执行过程中是否出现了任何失败。Run将一直阻塞直到f的所有并行测试执行完毕。
func (t *T) Parallel()
表示当前测试只会与其他带有Parallel方法的测试并行进行测试。

func (b *B) Run(name string, f func(b *B)) bool
执行名字为name的子基准测试f ,并报告f在执行过程中是否出现了任何失败。
func (b *B) RunParallel(body func(*PB))
以并行的方式执行给定的基准测试。会创建出多个goroutine,并将b.N分配给这些goroutine执行,goroutine数量的默认值为GOMAXPROCS 。
用户如果想要增加非CPU受限(non-CPU-bound)基准测试的并行性, 那么可以在RunParallel之前调用SetParallelism。
func (b *B) SetParallelism(p int)
将RunParallel使用的goroutine数量设置为p*GOMAXPROCS,如果p小于1调用将不产生任何效果。
func (b *B) ReportAllocs()
打开当前基准测试的内存统计功能,与使用-benchmem设置类似,但只影响那些调用了该函数的基准测试。

func (b *B) StopTimer()
停止对测试进行计时。当需要执行一些复杂的初始化操作,并且不想对这些操作进行测量时,就可以使用这个方法来暂时地停止计时。
func (b *B) StartTimer()
开始对测试进行计时。这个函数在基准测试开始时会自动被调用,它也可以在调用StopTimer之后恢复进行计时。
func (b *B) ResetTimer()
对已经过去的基准测试时间以及内存分配计数器进行清零。如果在运行前基准测试需要一些耗时的配置,则可以配置之后调用此函数。