《The Way to Go》Chapter13 错误处理与测试
Chapter 13 错误处理与测试
错误处理与测试
Go通常在函数和方法中返回错误对象作为返回值,如果返回nil则没有错误发生
永远不要忽略错误,否则可能会导致程序崩溃!调用函数后必须检查错误
为了让代码更清晰,应该使用包含错误值变量的if 符合语句
if value, err := pack1.Func1(param1); err != nil { fmt.Printf("Error %s in pack1.Func1 with parameter %v", err.Error(), param1) return // or: return err } else { // Process(value) }
错误处理
使用errors包来创建一个错误类型对象,附带上错误信息
err := errors.New("math - square root of negative number")
自定义一个错误结构,除了低层级错误信息还附带一些额外信息;在不同错误可能发生的场景下,对错误使用类型断言/类型判断(type-switch)
// PathError records an error and the operation and file path that caused it. type PathError struct { Op string // "open", "unlink", etc. Path string // The associated file. Err error // Returned by the system call. 低层级错误信息 } // err != nil if e, ok := err.(*os.PathError); ok { // remedy situation }
运行时异常和panic
- 当发生了需要将程序停止的错误时,使用
panic()
产生一个中止程序的运行时错误。 - 传递任意类型的参数给
panic()
,会在程序终止的信息中被打印出来。 panic()
所在函数的后一行代码不会执行,但panic会把执行控制权交还给上层,并执行每层的defer,这个过程叫做panicking
从panic中恢复(recover)
recover
只能在defer
修饰的函数中使用,如果发生了panic,那么defer
修饰的函数会执行,且recover
将得到对应的错误值,否者将返回nil并且没有其他效果
func protect(g func()) {
defer func() {
log.Println("done")
// Println executes normally even if there is a panic
if err := recover(); err != nil {
log.Printf("run time panic: %v", err)
} // 这里之后执行protect()之后的指令
}()
log.Println("start")
g() // possible runtime-error
}
自定义包中的错误处理和panicking
应该遵守的最佳实践:
- 在包内部,总是应该从panic中recover,不允许显式的超出包范围的panic()
- 向包的调用者返回错误值(而不是panic)
Go中的单元测试和基准测试
测试使用的工具是gotest,使用名为testing
的包,测试代码和包中的业务代码是分开的,_test
代码与程序不会被普通的Go编译器编译,只有gotest会编译所有的程序。
使用go
test
来编译测试程序,对于通过的测试会打印PASS,对于go test
-test.bench=.*
会运行基准测试,调用N次并展示N次的值和执行的平均时间。
性能调试:分析并优化Go程序
unix命令行中可以使用
time
命令,输出程序的执行时间和内存占用等使用Go中的
testing
包进行基准测试,使用-cpuprofile / -memprofile 标志向指定文件写入CPU或内存使用情况报告go test -x -v -cpuprofile=prof.out -file x_test.go
可以在程序中引入
runtime/pprof
包,这个包以pprof可视化工具需要的格式写入运行时报告数据。gopprof是Google pprofC++ 分析器的一个轻微变种,如果开启此性能分析,Go程序会以大约每秒100次的频率阻塞。为实现性能分析,你可以像下面这样添加代码:
var cpuprofile = flag.String("cpuprofile", "", "write cpu profile to file")
func main() {
flag.Parse()
if *cpuprofile != "" {
f, err := os.Create(*cpuprofile)
if err != nil {
log.Fatal(err)
}
pprof.StartCPUProfile(f)
defer pprof.StopCPUProfile()
}
...