【go】测试go test

目录

前言

1.go test

1.1 测试函数test

1.2 黑盒&白盒

1.3 测试配置

1.4 独立测试包

2.测试覆盖率

3.基准测试

4.pprof剖析


前言

很多语言都有测试工具,如Java的JUnit测试框架的自动化测试,也就是写一些小的程序用来检测产品代码行为和预期一致,设计执行某些特定的功能case或者是通过随机输入验证边界

Go的测试技术相对低级,依赖go test命令和一系列测试函数规范(convention)。这种相对轻量级的机制高效,且易扩展到基准测试和示例文档。

1.go test

每个包都可以有其测试代码,go test命令会遍历所有的*_test.go文件中符合上述命名规则的函数,生成一个临时的main包用于调用相应的测试函数,接着构建并运行、报告测试结果,最后清理测试中生成的临时文件。

【go】测试go test_第1张图片【go】测试go test_第2张图片

*_test.go文件中,有三种类型的函数:测试函数、基准测试(benchmark)函数、示例函数。

函数 功能
测试tests

Test*(t *testing.T)

逻辑行为是否正确

基准benchmarks

Benchmark*()

性能:多次运行基准函数以计算一个平均的执行时间

示例examples

Example*()

提供一个由编译器保证正确性的示例文档

1.1 测试函数test

每个测试函数必须导入testing包,命名以Test为前缀,后缀必须首字母大写。

func TestName(t *testing.T) {
    // ...
}

t参数用于报告测试失败信息和附加的日志信息 

  1. t.Error报告失败

【go】测试go test_第3张图片

go test选项 功能
-v 打印每个测试函数的名字和运行时间
-run egrex

只执行正则匹配的测试函数 

go test -v -run="French|Canal"

通常可以设计多个case,给定输入和预期结果,t.Errorf输出错误信息;和其他编程语言或测试框架的assert断言不同,t.Errorf调用也没有引起panic异常或停止测试的执行。当需要停止时,可以使用t.Fatal或t.Fatalf停止当前测试函数。

func TestIsPalindrome(t *testing.T) {
    // 功能case设计
	var tests = []struct {
		input string    // 输入
		want  bool       // 预期结果
	}{
		{"", true},
		{"a", true},
		{"aa", true},
		{"ab", false},
		...
		{"été", true},
		{"Et se resservir, ivresse reste.", true},
		{"palindrome", false}, // non-palindrome
		{"desserts", false},   // semi-palindrome
	}
	for _, test := range tests {
		if got := IsPalindrome(test.input); got != test.want {
			t.Errorf("IsPalindrome(%q) = %v", test.input, got)    // 错误信息,不终止执行
		}
	}
}

1.2 黑盒&白盒

黑盒和白盒这两种测试方法是互补的,

黑盒测试只需要测试包公开的文档和API行为,不关心内部实现;

白盒测试有访问包内部函数和数据结构的权限,因此可以做到一些普通客户端无法实现的测试。

黑盒测试一般更健壮,随着软件实现的完善,测试代码很少需要更新。它们可以帮助测试者了解真实客户的需求,也可以帮助发现API设计的一些不足之处。相反,白盒测试可以对内部一些棘手的实现提供更多的测试覆盖

以上的测试函数是黑盒测试,因为仅使用了包导出的函数,白盒(同一个包)可以调用未导出的函数,访问未导出变量。

1.3 测试配置

通常生产和测试的配置需要区别,如db路径、邮件等,如果写在全局变量,需要在测试代码中进行覆盖和恢复。

下面处理模式可以用来暂时保存恢复所有的全局变量,包括命令行标志参数、调试选项和优化参数;安装和移除导致生产代码产生一些调试信息的钩子函数;还有有些诱导生产代码进入某些重要状态的改变,比如超时、错误,甚至是一些刻意制造的并发行为等因素。

func TestCheckQuotaNotifiesUser(t *testing.T) {
    // Save and restore original notifyUser.
    saved := notifyUser
    defer func() { notifyUser = saved }()

    // Install the test's fake notifyUser.
    var notifiedUser, notifiedMsg string
    notifyUser = func(user, msg string) {
        notifiedUser, notifiedMsg = user, msg
    }
    // ...rest of test...
}

1.4 独立测试包

Go语言规范是禁止包的循环依赖的,如果A包的测试代码依赖B包,B包已经A包,这样就形成了循环依赖,所以go提供了外部测试包的方式解决这个问题。可以在A包所在路径创建A_test外部包,其中包名的_test后缀告诉go test工具它应该建立一个额外的包来运行测试。外部测试包不能被其余的导入引用。

外部测试包可以更灵活地编写测试,特别是集成测试(需要测试多个组件之间的交互),可以像普通应用程序那样自由地导入其他包。

2.测试覆盖率

语句的覆盖率是指在测试中至少被运行一次的代码总代码数的比例。

go test -run=Converage 标志参数通过在测试代码中插入生成钩子来统计覆盖率数据。在运行每个测试前,它将待测代码拷贝一份并做修改,在每个词法块都会设置一个布尔标志变量。当被修改后的被测试代码运行退出时,将统计日志数据写入c.out文件,并打印一部分执行的语句的一个总结。

// 用-coverprofile标志参数,生成覆盖率文件c.out
go test -run=Coverage -coverprofile=c.out gopl.io/ch7/eval
// 测试覆盖率工具,打印了测试日志,生成一个HTML报告
go tool cover -html=c.out

go tool cover 测试覆盖率工具,打印了测试日志,生成一个HTML报告。

【go】测试go test_第4张图片

绿色的代码块被测试覆盖到了,红色的则表示没有被覆盖到。

3.基准测试

//!+bench
func BenchmarkIsPalindrome(b *testing.B) {
	for i := 0; i < b.N; i++ {
		IsPalindrome("A man, a plan, a canal: Panama")
	}
}
cd $GOPATH/src/gopl.io/ch11/word2
go test -bench=.
go test -bench=. -benchmem    // 内存统计

通过-bench命令行标志参数手工指定要运行的基准测试函数,其中“.”模式将可以匹配所有基准测试函数。以下表示迭代到5000000次,耗时1.749s,平均

-benchmem命令行标志参数将在报告中包含内存的分配数据统计。

4.pprof剖析

go tool pprof能够分析出最消耗的函数

go test -run=NONE -bench=ClientServerParallelTLS64 -cpuprofile=cpu.log net/http

go tool pprof -text -nodecount=10 ./http.test cpu.log// 剖析日志文件

【go】测试go test_第5张图片

你可能感兴趣的:(go)