一.测试
1.传统测试 VS 表格驱动测试
传统测试 表格驱动测试
1.测试数据与测试逻辑混在一起 1.分离的测试数据和测试逻辑
2.出错信息不明确 2.明确的出错信息
3.一旦一个数据出错测试全部结束 3.可以部分失败
go语言的语法使得我们更容易实践表格驱动测试
传统测试 表格驱动测试
@Test public void testAdd(){ tests := [ ] struct {a, b, c int32} {
assertEquals(3, add(1, 2)); {1, 2, 3},
assertEquals(2, add(0, 2)); {0, 2, 2},
assertEquals(0, add(0, 0)); {0, 0, 0},
assertEquals(0, add(-1, 1)); {-1, 1, 0},
assertEquals(Integer.MIN_VALUE, {math.MaxInt32, 1, math.MinInt32},
add(1, Integer.MAX_VALUE)); }
} for _, test := range tests { if actual := add(test.a, test.b); actual != test.c { } }
2. 测试文件
- "testing"包 提供对 Go 包的自动化测试的支持。
- 在终端进入要测试的文件包中,通过 `go test` 命令,能够自动执行如下形式的任何函数:
func TestXxx(*testing.T)
注:其中 Xxx 可以是任何字母数字字符串(但第一个字母不能是 [a-z]),用于识别测试例程。
- 在这些函数中,使用 Error, Fail 或相关方法来进行错误说明。要编写一个新的测试套件,需要创建一个名称以 _test.go 结尾的文件,该文件包含 `TestXxx` 函数,如上所述。 将该文件放在与被测试的包相同的包中。该文件将被排除在正常的程序包之外,但在运行 “go test” 命令时将被包含。 有关详细信息,请运行 “go help test” 和 “go help testflag” 了解。
package main
import (
"math"
"fmt"
)
func Triangle(a, b int) int {
c := int(math.Sqrt(float64(a*a+b*b)))
return c
}
func main(){
fmt.Println(Triangle(3, 4))
}
//测试文件
package main
import (
"testing"
)
func TestTriangle(t *testing.T){
tests := []struct{
a, b, c int
}{
{3, 4, 5},
{5, 12, 13},
{8, 15, 17},
{12, 35, 37},
}
for _, tt := range tests {
if actual := Triangle(tt.a, tt.b); actual != tt.c{
t.Errorf("Triangle(%d, %d) got %d, expacted %d", tt.a, tt.b, actual, tt.c)
}
}
}
3.查看代码覆盖率
命令行输入:
go test -coverprofile=c.out // 生成覆盖率文件c.out
go tool cover -html=c.out //使用 go tool 来查看c,out
4.http测试
- 通过使用假的 Request / Response
- 使用"ne/http/testing"包,通过起服务器
二.性能优化
1.基准测试(Benchmarks)
- 如下形式的函数:
func BenchmarkXxx(*testing.B)
被认为是基准测试,可以与测试函数 "func TestXxx(*testing.T)" 放在同一个文件中,通过 "go test" 命令,加上 -bench flag 来执行 (go test -bench . )。多个基准测试按照顺序运行。
testing flags 的详细描述, 参见 https://github.com/golang/go/blob/master/cmd/go/#hdr-Description_of_testing_flags.
- 基准测试函数样例看起来如下所示:
func BenchmarkHello(b *testing.B) { for i := 0; i < b.N; i++ { fmt.Sprintf("hello") // 待测试的代码段 } }
基准函数会运行目标代码 b.N 次。在基准执行期间,会调整 b.N 直到基准测试函数持续足够长的时间。输出
BenchmarkHello 10000000 282 ns/op
意味着循环执行了 10000000 次,每次循环花费 282 纳秒(ns)。
- 如果在运行前基准测试需要一些耗时的配置,则可以先重置定时器:
func BenchmarkBigLen(b *testing.B) { big := NewBig() b.ResetTimer() for i := 0; i < b.N; i++ { big.Len() } }
2.具体优化
命令行输入:
go test -bench . -cpuprofile cpu.out // 生成记录cpu具体执行时间文件 cpu.out (二进制文件)
go tool pprof cpu.out //打开 pprof 命令行
在 pprof 命令行下输入: web // 在web页面显示cpu时间分配图,可以针对cpu占用时间长的代码进行优化
注: pprof 下使用 web 需安装 "graphviz" 下载:Download | Graphviz
更多 pprof 命令 可以 输入"help"查看
三. 生成文档
- 在代码中写的注释可以生成文档
- 在测试文件中写的Examples既可以用来做测试,同时也可以将example生成在文档中
- 使用 go doc / godoc 来 查看 / 生成 文档
命令行输入:godoc 包名 函数名 //直接在终端查看某个包或某函数的注释文档
godoc -http :6060 //在 http://localhost:6060 查看文档
Examples
- 该包运行并验证示例代码。示例函数可以包括以 "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 }
- 没有输出注释的示例函数被编译但不执行。
- example 声明的命名约定:包,函数 F,类型 T,类型 T 上的方法 M 依次是:
func Example() { ... } func ExampleF() { ... } func ExampleT() { ... } func ExampleT_M() { ... }
- 可以为 包/类型/函数/方法 提供多个 example 函数,这通过在名称上附加一个不同的后缀来实现。后缀必须是以小写字母开头。
func Example_suffix() { ... } func ExampleF_suffix() { ... } func ExampleT_suffix() { ... } func ExampleT_M_suffix() { ... }
- 当一个文件包含一个示例函数,同时至少一个其他函数,类型,变量或常量声明,或没有测试或基准函数时,这个测试文件作为示例存在,通常命名为 example_test.go