文章目录
Go 程序测试
Go 语音由自带的测试工具。标准库中提供了转变用于测试的代码包testing。和go test 命令进行协调使用,可以自动执行目标代码包中的任何测试函数。用于测试的代码文件必须以"_test"作为后缀,否则命令会找不到测试文件。
1 功能测试
1.1 编写功能测试函数
- 计算器 main.go
package main
import "fmt"
func add(a, b int) {
c:= a+b
fmt.Println(c)
}
func subtract(a , b int) {
c := a-b
fmt.Println(c)
}
func multiply(a , b int) {
c := a*b
fmt.Println(c)
}
func divide(a , b int) {
c := a/b
fmt.Println(c)
}
func main() {
}
- 测试文件 main_test.go
package main
import "testing"
func TestAdd(t *testing.T) {
a := 2
b :=3
add(a,b)
}
func TestSubtract(t *testing.T) {
a := 2
b :=3
subtract(a,b)
}
func TestMultiply(t *testing.T) {
a := 2
b :=3
multiply(a,b)
}
func TestDivide(t *testing.T) {
a := 2
b :=3
divide(a,b)
}
1.2 进一步完善
参数类型 *testing.T,测试包testing的API,T 是传递给测试函数的一种类型,它用于管理测试状态并支持格式化测试日志。测试日志会在执行测试的过程中不断累积, 并在测试完成时转储至标准输出。
当一个测试的测试函数返回时, 又或者当一个测试函数调用 FailNow 、 Fatal 、 Fatalf 、 SkipNow 、 Skip 或者 Skipf 中的任意一个时, 该测试即宣告结束。 跟 Parallel 方法一样, 以上提到的这些方法只能在运行测试函数的 goroutine 中调用。
至于其他报告方法, 比如 Log 以及 Error 的变种, 则可以在多个 goroutine 中同时进行调用。
1.2.1 常规记录
参数t上的Log和Logf一般用于记录一些常规信息,以展现测试程序的运行过程以及被测试程序实体的实时状态。t.Log方法与fmt.Println函数使用方法类型,而t.Logf方法则与fmt.Printf函数的使用方法类似。
t.Name(),返回正在运行的测试或基准测试的名字。
/*
* @Descirption:
* @Author: Lichangrui
* @Email: 137685472@qq.com
* @Date: 2019-01-19 23:41:49
* @LastEditTime: 2019-01-20 08:51:42
*/
package main
import "testing"
func TestAdd(t *testing.T) {
t.Log(t.Name(), "Begin...")
a := 2
b := 3
add(a, b)
t.Logf("a + b is : %d ", a+b)
t.Log(t.Name(), "End...")
}
func TestSubtract(t *testing.T) {
t.Log(t.Name(), "Begin...")
a := 2
b := 3
subtract(a, b)
t.Logf("a - b is : %d ", a-b)
t.Log(t.Name(), "Begin...")
}
func TestMultiply(t *testing.T) {
t.Log(t.Name(), "Begin...")
a := 2
b := 3
multiply(a, b)
t.Logf("a * b is : %d ", a*b)
t.Log(t.Name(), "Begin...")
}
func TestDivide(t *testing.T) {
t.Log(t.Name(), "Begin...")
a := 2
b := 3
divide(a, b)
t.Logf("a / b is : %d ", a/b)
t.Log(t.Name(), "Begin...")
}
运行 go test -v 命令,测试结果:
$ go test -v
=== RUN TestAdd
5
--- PASS: TestAdd (0.00s)
main_test.go:13: TestAdd Begin...
main_test.go:17: a + b is : 5
main_test.go:18: TestAdd End...
=== RUN TestSubtract
-1
--- PASS: TestSubtract (0.00s)
main_test.go:22: TestSubtract Begin...
main_test.go:26: a - b is : -1
main_test.go:27: TestSubtract Begin...
=== RUN TestMultiply
6
--- PASS: TestMultiply (0.00s)
main_test.go:31: TestMultiply Begin...
main_test.go:35: a * b is : 6
main_test.go:36: TestMultiply Begin...
=== RUN TestDivide
0
--- PASS: TestDivide (0.00s)
main_test.go:40: TestDivide Begin...
main_test.go:44: a / b is : 0
main_test.go:45: TestDivide Begin...
PASS
ok goChannel/main 0.004s
1.2.2 错误记录
参数t的Error和Errorf方法被用来记录错误信息。当被测试程序实体状态不正确的时候,就应该调用t.Error和t.Errorf方法,及时度当前的错误状态进行记录。修改一下代码。返回计算结果,并把add改成“-” 法。
package main
func add(a, b int) int {
c := a - b
return c
}
func subtract(a, b int) int {
c := a - b
return c
}
func multiply(a, b int) int {
c := a * b
return c
}
func divide(a, b int) int {
c := a / b
return c
}
func main() {
}
- 修改测试代码
func TestAdd(t *testing.T) {
t.Log(t.Name(), "Begin...")
a := 2
b := 3
if add(a, b) != 5 {
t.Errorf("Error: add(2, 5) shuould be 5 ,but result is : %d", add(a, b))
}
t.Logf("a + b is : %d ", a+b)
t.Log(t.Name(), "End...")
}
- 运行测试
$ go test -v
=== RUN TestAdd
--- FAIL: TestAdd (0.00s)
main_test.go:13: TestAdd Begin...
main_test.go:17: Error: add(2, 5) shuould be 5 ,but result is : -1
main_test.go:19: a + b is : 5
main_test.go:20: TestAdd End...
=== RUN TestSubtract
--- PASS: TestSubtract (0.00s)
main_test.go:24: TestSubtract Begin...
main_test.go:28: a - b is : -1
main_test.go:29: TestSubtract Begin...
=== RUN TestMultiply
--- PASS: TestMultiply (0.00s)
main_test.go:33: TestMultiply Begin...
main_test.go:37: a * b is : 6
main_test.go:38: TestMultiply Begin...
=== RUN TestDivide
--- PASS: TestDivide (0.00s)
main_test.go:42: TestDivide Begin...
main_test.go:46: a / b is : 0
main_test.go:47: TestDivide Begin...
FAIL
exit status 1
FAIL goChannel/main 0.004s
1.2.3 致命错误记录
参数t的Fatl 和 Fatalf方法被用于记录致命的错误,所谓致命错误是指使测试无法继续进行的错误。
除法函数,如果除数是0,则会出现panic错误。修改一下测试代码
func TestDivide(t *testing.T) {
t.Log(t.Name(), "Begin...")
a := 2
b := 0
if b == 0 {
t.Fatalf("Fatalf b is 0")
}
divide(a, b)
t.Logf("a / b is : %d ", a/b)
t.Log(t.Name(), "Begin...")
}
- 运行测试:
$ go test -v
=== RUN TestAdd
--- FAIL: TestAdd (0.00s)
main_test.go:13: TestAdd Begin...
main_test.go:17: Error: add(2, 5) shuould be 5 ,but result is : -1
main_test.go:19: a + b is : 5
main_test.go:20: TestAdd End...
=== RUN TestSubtract
--- PASS: TestSubtract (0.00s)
main_test.go:24: TestSubtract Begin...
main_test.go:28: a - b is : -1
main_test.go:29: TestSubtract Begin...
=== RUN TestMultiply
--- PASS: TestMultiply (0.00s)
main_test.go:33: TestMultiply Begin...
main_test.go:37: a * b is : 6
main_test.go:38: TestMultiply Begin...
=== RUN TestDivide
--- FAIL: TestDivide (0.00s)
main_test.go:42: TestDivide Begin...
main_test.go:46: Fatalf b is 0
FAIL
exit status 1
FAIL goChannel/main 0.004s
如果注释掉
if b == 0 {
t.Fatalf("Fatalf b is 0")
}
运行测试,出现panic错误,TestDivide的测试异常中断:
$ go test -v
=== RUN TestAdd
--- FAIL: TestAdd (0.00s)
main_test.go:13: TestAdd Begin...
main_test.go:17: Error: add(2, 5) shuould be 5 ,but result is : -1
main_test.go:19: a + b is : 5
main_test.go:20: TestAdd End...
=== RUN TestSubtract
--- PASS: TestSubtract (0.00s)
main_test.go:24: TestSubtract Begin...
main_test.go:28: a - b is : -1
main_test.go:29: TestSubtract Begin...
=== RUN TestMultiply
--- PASS: TestMultiply (0.00s)
main_test.go:33: TestMultiply Begin...
main_test.go:37: a * b is : 6
main_test.go:38: TestMultiply Begin...
=== RUN TestDivide
--- FAIL: TestDivide (0.00s)
main_test.go:42: TestDivide Begin...
panic: runtime error: integer divide by zero [recovered]
panic: runtime error: integer divide by zero
goroutine 8 [running]:
testing.tRunner.func1(0xc0000a0400)
/usr/local/go/src/testing/testing.go:792 +0x387
panic(0x1112480, 0x121b0f0)
/usr/local/go/src/runtime/panic.go:513 +0x1b9
goChannel/main.divide(...)
/Users/changruili/go/src/goChannel/main/main.go:26
goChannel/main.TestDivide(0xc0000a0400)
/Users/changruili/go/src/goChannel/main/main_test.go:48 +0xc0
testing.tRunner(0xc0000a0400, 0x113f560)
/usr/local/go/src/testing/testing.go:827 +0xbf
created by testing.(*T).Run
/usr/local/go/src/testing/testing.go:878 +0x35c
exit status 2
FAIL goChannel/main 0.007s
1.2.4 失败标记
参数t的方法t.Fail和t.FailNow被用来标记测试结果为失败的,不同的是t.Fail只把测试结果标记为失败,不会终止当前测试,t.FailNow则会立即终止当前的测试函数,并标记为失败。
- 修改测试代码:
func TestSubtract(t *testing.T) {
t.Fail()
t.Log(t.Name(), "Begin...")
a := 2
b := 3
subtract(a, b)
t.Logf("a - b is : %d ", a-b)
t.Log(t.Name(), "Begin...")
}
func TestMultiply(t *testing.T) {
t.FailNow()
t.Log(t.Name(), "Begin...")
a := 2
b := 3
multiply(a, b)
t.Logf("a * b is : %d ", a*b)
t.Log(t.Name(), "Begin...")
}
- 运行测试:
$ go test -v
=== RUN TestAdd
--- FAIL: TestAdd (0.00s)
main_test.go:13: TestAdd Begin...
main_test.go:17: Error: add(2, 5) shuould be 5 ,but result is : -1
main_test.go:19: a + b is : 5
main_test.go:20: TestAdd End...
=== RUN TestSubtract
--- FAIL: TestSubtract (0.00s)
main_test.go:25: TestSubtract Begin...
main_test.go:29: a - b is : -1
main_test.go:30: TestSubtract Begin...
=== RUN TestMultiply
--- FAIL: TestMultiply (0.00s)
=== RUN TestDivide
--- PASS: TestDivide (0.00s)
main_test.go:44: TestDivide Begin...
main_test.go:48: a / b is : 0
main_test.go:49: TestDivide Begin...
FAIL
exit status 1
FAIL goChannel/main 0.004s
可以看到,TestSubtract虽然标记为失败了,但依然执行了测试函数,但是TestMultiply直接标记失败,没有执行测。通过调头t.Failed()方法,可以得到一个bool类型的返回值,表示当前的测试函数中的测试是否已经被标记为失败。
1.2.5 忽略测试
在实际开发中,我们会新增一些功能,之前的测试已经进行过,不需要在运行,我们只想看新增的测试信息,需要忽略之前的测试,但是又要保留测试代码,或者我们只想看某个测试,可以通过t.Skip和t.SkipNow、t.Skipf标记测试。t.Skip相当于先调用t.Lop再调用t.SkipNow,t.Skipf则相当于先调用t.Logf再调用t.SkipNow。
- 修改测试测试代码:
package main
import "testing"
func TestAdd(t *testing.T) {
t.SkipNow()
t.Log(t.Name(), "Begin...")
a := 2
b := 3
if add(a, b) != 5 {
t.Errorf("Error: add(2, 5) shuould be 5 ,but result is : %d", add(a, b))
}
t.Logf("a + b is : %d ", a+b)
t.Log(t.Name(), "End...")
}
func TestSubtract(t *testing.T) {
t.Skipf("不想测试你: %s", t.Name())
t.Fail()
t.Log(t.Name(), "Begin...")
a := 2
b := 3
subtract(a, b)
t.Logf("a - b is : %d ", a-b)
t.Log(t.Name(), "Begin...")
}
func TestMultiply(t *testing.T) {
t.Log(t.Name(), "Begin...")
a := 2
b := 3
multiply(a, b)
t.Logf("a * b is : %d ", a*b)
t.Log(t.Name(), "Begin...")
}
func TestDivide(t *testing.T) {
t.Log(t.Name(), "Begin...")
a := 2
b := 3
divide(a, b)
t.Logf("a / b is : %d ", a/b)
t.Log(t.Name(), "Begin...")
}
- 运行测试:
$ go test -v
=== RUN TestAdd
--- SKIP: TestAdd (0.00s)
=== RUN TestSubtract
--- SKIP: TestSubtract (0.00s)
main_test.go:25: 不想测试你: TestSubtract
=== RUN TestMultiply
--- PASS: TestMultiply (0.00s)
main_test.go:36: TestMultiply Begin...
main_test.go:40: a * b is : 6
main_test.go:41: TestMultiply Begin...
=== RUN TestDivide
--- PASS: TestDivide (0.00s)
main_test.go:45: TestDivide Begin...
main_test.go:49: a / b is : 0
main_test.go:50: TestDivide Begin...
PASS
ok goChannel/main 0.004s
前两个测试函数已经被标记为SKIP,并跳过测试。
1.2.6 并行运行
之前的测试都是顺序进行的,有的时候我们的某些功能依赖于前一个功能的结果,但有的时候不需要,尤其是当测试用例非常多时,我们希望可以并行运行测试,以便加快测试进度,通过调用t.Parallel,可以把测试标记为可以并发执行的,可以让测试并发d地执行它以及其他科并行的函数,修改测试代码:
package main
import "testing"
func TestAdd(t *testing.T) {
t.Parallel()
t.Log(t.Name(), "Begin...")
a := 2
b := 3
if add(a, b) != 5 {
t.Errorf("Error: add(2, 5) shuould be 5 ,but result is : %d", add(a, b))
}
t.Logf("a + b is : %d ", a+b)
t.Log(t.Name(), "End...")
}
func TestSubtract(t *testing.T) {
t.Parallel()
t.Log(t.Name(), "Begin...")
a := 2
b := 3
subtract(a, b)
t.Logf("a - b is : %d ", a-b)
t.Log(t.Name(), "Begin...")
}
func TestMultiply(t *testing.T) {
t.Parallel()
t.Log(t.Name(), "Begin...")
a := 2
b := 3
multiply(a, b)
t.Logf("a * b is : %d ", a*b)
t.Log(t.Name(), "Begin...")
}
func TestDivide(t *testing.T) {
t.Parallel()
t.Log(t.Name(), "Begin...")
a := 2
b := 3
divide(a, b)
t.Logf("a / b is : %d ", a/b)
t.Log(t.Name(), "Begin...")
}
- 运行测试:
$ go test -v
=== RUN TestAdd
=== PAUSE TestAdd
=== RUN TestSubtract
=== PAUSE TestSubtract
=== RUN TestMultiply
=== PAUSE TestMultiply
=== RUN TestDivide
=== PAUSE TestDivide
=== CONT TestAdd
=== CONT TestDivide
=== CONT TestSubtract
=== CONT TestMultiply
--- PASS: TestDivide (0.00s)
main_test.go:46: TestDivide Begin...
main_test.go:50: a / b is : 0
main_test.go:51: TestDivide Begin...
--- PASS: TestSubtract (0.00s)
main_test.go:26: TestSubtract Begin...
main_test.go:30: a - b is : -1
main_test.go:31: TestSubtract Begin...
--- FAIL: TestAdd (0.00s)
main_test.go:14: TestAdd Begin...
main_test.go:18: Error: add(2, 5) shuould be 5 ,but result is : -1
main_test.go:20: a + b is : 5
main_test.go:21: TestAdd End...
--- PASS: TestMultiply (0.00s)
main_test.go:36: TestMultiply Begin...
main_test.go:40: a * b is : 6
main_test.go:41: TestMultiply Begin...
FAIL
exit status 1
FAIL goChannel/main 0.004s
测试同时进行,测试输出显得比较混乱,测试的输出也没有按照测试函数在测试文件中声名的顺序进行,而是随机的。
2 基准测试
所谓基准测试(Benchmark Test,简称BMT)是指通过一些科学的手段实现对一类测试对象的某项性能指标进行可测量、可重复和可对比的测试。因此肌醇测试也被狭义的叫做性能测试
- 基准测试的函数声明 :func BenchmarkXxx(b *testing.B)
跟功能测试不同,函数名总是以Benchmark开头,其次参数类型是 testing.B 而不是testing.T。
- 计时器
在*testing.B 中有3个定时器方法:StartTimer、StopTimer和ResetTimer。计时器的作用是计算当前基准测试函数的执行时间。
调用b.StartTimer方法意味着开始对当前的测试函数的执行进行计时。会在开始执行基准测试函数的时候被自动调用。因此这些方法被暴露出来的意义在意计时器被停止后重新启动。调用b.StopTimer方法可以使当前测试函数的计时停止。
下面我们写一个二分查找,来进行测试
func find(slice []int, k int) int {
left, right, mid := 1, len(slice), 0
for {
// mid向下取整
mid = int(math.Floor(float64((left + right) / 2)))
if slice[mid] > k {
// 如果当前元素大于k,那么把right指针移到mid - 1的位置
right = mid - 1
} else if slice[mid] < k {
// 如果当前元素小于k,那么把left指针移到mid + 1的位置
left = mid + 1
} else {
// 否则就是相等了,退出循环
break
}
// 判断如果left大于right,那么这个元素是不存在的。返回-1并且退出循环
if left > right {
mid = -1
break
}
}
// 输入元素的下标
return mid
}
func main() {
}
- 测试代码:
import (
"fmt"
"testing"
)
func BenchmarkFind(b *testing.B) {
//b.StopTimer()
mySlice := []int{1, 2, 5, 7, 15, 25, 30, 36, 39, 51, 67, 78, 80, 82, 85, 91, 92, 97}
var k = 67
var index int
for i := 0; i < b.N; i++ {
index = find(mySlice, k)
}
if index != -1 {
fmt.Printf("找到 %d 位置为: %d\n", k, index)
} else {
fmt.Println("没有找到!")
}
}
- 先停止计时,,然后在把停止计时的代码注释掉,看两次对比结果,测试结果:
$ go test -bench="."
找到 67 位置为: 10
goos: darwin
goarch: amd64
pkg: goChannel/main
BenchmarkFind-8 找到 67 位置为: 10
找到 67 位置为: 10
找到 67 位置为: 10
找到 67 位置为: 10
找到 67 位置为: 10
2000000000 0.00 ns/op
PASS
ok goChannel/main 32.758s
$ go test -bench="."
找到 67 位置为: 10
goos: darwin
goarch: amd64
pkg: goChannel/main
BenchmarkFind-8 找到 67 位置为: 10
找到 67 位置为: 10
找到 67 位置为: 10
找到 67 位置为: 10
100000000 14.8 ns/op
PASS
ok goChannel/main 1.501s
- 测试会先执行一次,用于产生一个执行的时间,当这个时间比Go默认的执行时间长时,测试函数只执行一次,否则执行多次。b.N是用于执行单次测试的代码循环次数,这个值也是Go自动生成的,执行的次数也不一定,在使用b.StopTimer()时,测试函数只运行了2次,N是20亿,执行的时间是ns/op,使用了b.StopTimer是0.00.当注释掉后,测试函数执行了5次,第一次产生测试时间用于和默认时间比较,之后运行了4次,每次N的值是10亿,每次执行时间是14.8ns,总运行时间是1.051s.
关于 go test 的参数,之后再说。
- 关于内存统计:
b.ReportAllocs判断在启动测试的go test 命令后是否有-benchmem参数,返回一个bool值。
b.SetBytes接受一个int64类型的值它被用于记录在单次操作中被处理的字节数。改一下测试函数:
func BenchmarkFind(b *testing.B) {
//b.StopTimer()
mySlice := []int{1, 2, 5, 7, 15, 25, 30, 36, 39, 51, 67, 78, 80, 82, 85, 91, 92, 97}
var k = 67
var index int
b.SetBytes(1028374374)
for i := 0; i < b.N; i++ {
index = find(mySlice, k)
}
if index != -1 {
fmt.Printf("找到 %d 位置为: %d\n", k, index)
} else {
fmt.Println("没有找到!")
}
}
- 测试结果:
$ go test -bench="."
找到 67 位置为: 10
goos: darwin
goarch: amd64
pkg: goChannel/main
BenchmarkFind-8 找到 67 位置为: 10
找到 67 位置为: 10
找到 67 位置为: 10
找到 67 位置为: 10
100000000 14.8 ns/op 69472713501.11 MB/s
PASS
ok goChannel/main 1.504s
测试结果多了一个 MB/s ,这个方法在进行IO测试时功能很明显,可以进行读写程序的性能测试。b.SetBytes()只传入一个字节数,并不是真的分配内存,只是一个用于统计的int64整数。
3 样本测试
样本测试函数以"Example"开头。并且,在这类函数的函数体的最后还可以有若干注释行。这些注释行的作用是,比较在该函数被执行期间标准输出上出现的内容是否与预期相符。
但是要使得注释行能被正确匹配,必须满足一下条件:
- 1 注释行必须出现在函数体的末尾,且和当前函数的结束符”}"之间没有任何代码。否则无效,也就是说写在循环体或者块语句里面是无效的,即使这个循环在函数”}“结束符之前.
- 2 在第一行注释中,紧跟在单行注释符”//“之后的必须是"Output:"否则无效。
- 3 Output:右边的内容及后续的注释行的内容都分别代表了标准输出的中的一行内容。
例子:
func ExampleFunc() {
fmt.Println("Hello,World!")
fmt.Println("ExampleTest")
//Output: Hello,World!
//ExampleTes
}
func ExampleFunc() {
fmt.Println("Hello,World!")
fmt.Println("ExampleTest")
//Output: Hello,World!
//ExampleTes
}
- 比较两次的测试结果,一个失败并给出got和want信息,第二个完全匹配,测试通过(PASS):
$ go test -v
=== RUN ExampleTest
--- FAIL: ExampleTest (0.00s)
got:
Hello,World!
ExampleTest
want:
Hello,World!
ExampleTes
FAIL
exit status 1
FAIL goChannel/main 0.004s
ChangruideMacBook-Pro:main changruili$ go test -v
=== RUN ExampleFunc
--- PASS: ExampleFunc (0.00s)
PASS
ok goChannel/main 0.004s
4 go test 测试参数
参数 | 说明 |
---|---|
-bench regexp | 仅运行与正则表达式匹配的基准。默认情况下, 不运行基准测试。要运行所有基准测试,请使用’-bench’。 或’-bench = .’。正则表达式由未括号的斜杠(/)字符拆分为 正则表达式序列,并且基准测试标识符的每个部分必须与 序列中的相应元素匹配(如果有)。可能的匹配父项以 bN = 1运行以识别子基准。例如,给定-bench = X / Y,匹配X的 顶级基准测试以bN = 1 运行,以找到与Y匹配的任何子基准,然后完全运行。 |
-benchtime t | 运行每个基准测试的足够迭代以获取t,指定为time.Duration(例如,-benchtime 1h30s)。默认值为1秒(1秒) |
-count n | 运行每个测试和基准n次(默认值1)。如果设置了-cpu,则为每个GOMAXPROCS值运行n次。示例总是运行一次。 |
-cover | 启用覆盖率分析。请注意,由于覆盖率通过在编译之前注释源代码来工作,因此启用覆盖率的编译和测试失败可能会报告与原始源不对应的行号 |
-covermode set,count,atomic | 设置包的覆盖率分析模式[s]正在接受测试 除非启用了-race,否则默认为“set”,在这种情况下它是“原子”。 |
-coverpkg pattern1,pattern2,pattern3 | 将每个测试中的覆盖率分析应用于与模式匹配的包。默认情况是每个测试仅分析正在测试的包。有关包模式的说明,请参阅“go help packages”。设置 - 覆盖。 |
-cpu 1,2,4 | 指定要为其执行测试的GOMAXPROCS值列表 应该执行基准测试。默认值 是GOMAXPROCS 的当前值。 |
-failfast | 在第一次测试失败后不要开始新的测试。 |
-list regexp | 列出与正则表达式匹配的测试,基准或示例。 不会运行测试,基准测试或示例。这只 列出顶级测试。不会显示子测试或子基准测试。 |
-parallel n | 允许并行执行调用t.Parallel的测试函数。 该标志的值是 同时运行的最大测试数; 默认情况下,它设置为GOMAXPROCS的值。 请注意,-parallel仅适用于单个测试二进制文件。 ‘go test’命令也可以 根据-p标志的设置并行运行不同包的测试 (参见’go help build’)。 |
-run regexp | 仅运行与正则表达式匹配的那些测试和示例。 对于测试,正则表达式由未括号的斜杠(/) 字符拆分为正则表达式序列,并且 测试标识符的每个部分必须与 序列中的相应元素匹配(如果有)。请注意,匹配的可能父项也会 运行,因此-run = X / Y匹配并运行并报告 与X匹配的所有测试的结果,即使那些没有匹配Y的子测试, 因为它必须运行它们来寻找那些子测试。 |
-short | 告诉长时间运行的测试以缩短其运行时间。 默认情况下它处于关闭状态,但在all.bash期间设置,以便安装 Go tree可以运行完整性检查但不花时间运行 详尽的测试。 |
-timeout d | 如果测试二进制文件的运行时间超过持续时间d,则发生混乱。 如果d为0,则禁用超时。 默认值为10分钟(10m)。 |
-v | 详细输出:在运行时记录所有测试。 即使测试成功,也会打印Log和Logf调用中的所有文本。 |
-vet list | 在“go test”期间配置“go vet”的调用 使用以逗号分隔的兽医检查列表。 如果list为空,则“go test”运行“go vet”,其中列出了一系列 被认为总是值得解决的检查。 如果列表是“关闭”,则“go test”根本不会运行“go vet”。 |
-benchmem | 打印基准的内存分配统计信息。 |
-blockprofile block.out | 在所有测试完成后,将goroutine阻塞配置文件写入指定的文件。将测试二进制文件写为-c will。 |
-blockprofilerate n | 通过使用n调用runtime.SetBlockProfileRate来控制goroutine阻塞配置文件中提供的详细信息。 请参阅’go doc runtime.SetBlockProfileRate’。 分析器的目的是平均每隔 n纳秒对程序所阻塞的一个阻塞事件进行采样。默认情况下, 如果设置了-test.blockprofile而没有此标志, 则会记录所有阻塞事件,相当于-test.blockprofilerate = 1。 |
-coverprofile cover.out | 在所有测试通过后,将覆盖配置文件写入文件。设置 -cover。 |
-cpuprofile cpu.out | 在退出之前将CPU配置文件写入指定的文件。将测试二进制文件写为-c will。 |
-memprofile mem.out | 在所有测试通过后将分配配置文件写入文件。将测试二进制文件写为-c will。 |
-memprofilerate n | 通过设置runtime.MemProfileRate,启用更精确(和昂贵)的内存分配配置文件。请参阅’go doc runtime.MemProfileRate’。要分析所有内存分配,请使用-test.memprofilerate = 1。 |
-mutexprofile mutex.out | 所有测试完成后,将互斥锁争用配置文件写入指定的文件。将测试二进制文件写为-c will。 |
-mutexprofilefraction n | n堆栈中的样本1,包含 争用互斥锁的goroutines 。 |
-outputdir directory | 将分析中的输出文件放在指定目录中, 默认情况下是运行“go test”的目录。 |
-trace trace.out | 在退出之前将执行跟踪写入指定的文件 |
这些标志中的每一个也通过可选的“test”识别。前缀,如-test.v. 但是,当直接调用生成的测试二进制文件('go test -c’的结果)时,前缀是必需的。
在调用测试二进制文件之前,'go test’命令在可选包列表之前和之后,根据需要重写或删除已识别的标志。
例如,命令
go test -v -myflag testdata -cpuprofile = prof.out -x
将编译测试二进制文件,然后运行它
pkg.test -test.v -myflag testdata -test.cpuprofile = prof.out
(-x标志被删除,因为它仅适用于go命令的执行,而不适用于测试本身。)
生成配置文件的测试标志(覆盖范围除外)也会将测试二进制文件保留在pkg.test中,以便在分析配置文件时使用。
当’go test’运行测试二进制文件时,它会从相应软件包的源代码目录中执行。根据测试,在直接调用生成的测试二进制文件时可能需要执行相同操作。
命令行程序包列表(如果存在)必须出现在go test命令未知的任何标志之前。继续上面的例子,包列表必须出现在-myflag之前,但可能出现在-v的两侧。
当’go test’在包列表模式下运行时,'go test’会缓存成功的包测试结果,以避免不必要的重复运行测试。要禁用测试缓存,请使用除可缓存标志之外的任何测试标志或参数。显式禁用测试缓存的惯用方法是使用-count = 1。
要保持测试二进制文件的参数不被解释为已知标志或包名称,请使用-args(请参阅“go help test”),它将命令行的其余部分传递给未解释且未更改的测试二进制文件。
例如,命令
go test -v -args -x -v
将编译测试二进制文件,然后运行它
pkg.test -test.v -x -v
同样的,
go test -args matg
将编译测试二进制文件,然后运行它
pkg.test math