testing包
testing包提供了自动化测试相关的框架,测试源码文件的主名称通常已被测试源码文件的名字作为开头,文件名必须以xx_test.go结尾,例如我们的被测试源码文件名称是demo.go 那么我们测试源码文件名称应该是demo_test.go
功能测试test
1测试方法样式是func Testxxx(t *testing.T),方法名词必须以Test开头,xxx首字母需要大写,func TestFoo(t *testing.T)
2测试方法参数必须 t *testing.T,函数中通过调用testing.T的Error, Errorf和FailNow, Fatal, FatalIf等方法说明测试不通过,以error 打印函数不会终止测试,Fatal类型会造成该单元测试终止。
当然通过调用Log方法用来记录测试的信息。
eg:
import "testing"
func TestFoo(t *testing.T) {
t.Log("test")
}
测试代码 calc.go
package calc
func Add(a, b int) int {
return a + b
}
func Sub(a, b int) int {
return a - b
}
func Mul(a, b int) int {
return a * b
}
func Div(a, b int) int {
return a / b
}
单元测试代码
func TestAdd(t *testing.T) {
a := 1
b := 2
c := Add(a, b)
if c != 3 {
t.Fatalf("err:%d + %d =%d", a, b, c)
}
t.Logf("%d + %d =%d", a, b, c)
}
func TestSub(t *testing.T) {
a := 10
b := 2
c := Sub(a, b)
if c != 8 {
t.Fatalf("err:%d - %d = %d", a, b, c)
}
t.Logf("%d - %d =%d", a, b, c)
}
压力/性能测试benchmark
Benchmark*testing.B
import "testing"
func BenchmarkFoo(t *testing.B) {
t.Log("Benchmark")
}
性能测试demo
package test
import (
"bytes"
"strings"
"testing"
)
func BenchmarkByte(t *testing.B) {
b := bytes.Buffer{}
b.WriteString("foo")
for i := 0; i < t.N; i++ {
b.String()
}
}
func BenchmarkStr(t *testing.B) {
s := strings.Builder{}
s.WriteString("test")
for i := 0; i < t.N; i++ {
s.String()
}
}
-test_bench
go test bench_test.go
ok command-line-arguments 0.001s [no tests to run]
".*"表示测试全部的压力测试函数,执行当前测试文件的所有压力测试函数,第一列表示被执行的测试函数,-8代表当前的cup执行核数,第二列表示执行了总共次数,第三列表示平均执行的耗时
go test bench_test.go -test.bench=".*"
goos: linux
goarch: amd64
BenchmarkByte-8 200000000 6.17 ns/op
BenchmarkStr-8 2000000000 0.34 ns/op
PASS
ok command-line-arguments 2.577s
执行单个的测试函数
go test bench_test.go -test.bench="Str"
goos: linux
goarch: amd64
BenchmarkStr-8 2000000000 0.34 ns/op
PASS
ok command-line-arguments 0.719s
go test
go test +包名,执行这个包下面的所有测试用例
go test +测试源文件,执行这个测试源文件里的所有测试用例
go test -run选项,执行只定的测试用例
调试
delve是golang推荐的专门go语言调试工具,用来替代gdb,因为:golang组织说delve能更好的理解go语言。
xcode-select --install
go get github.com/derekparker/delve/cmd/dlv
当前调试程序如下
package main
import "net/http"
func hello(writer http.ResponseWriter, request *http.Request) {
host := request.Host
writer.Write([]byte(host))
}
func main() {
http.HandleFunc("/hello",hello)
http.ListenAndServe(":8080",nil)
}
bcurl localhost:8080/hello
(dlv) b main.hello
Breakpoint 1 set at 0x1330e73 for main.hello() ./main.go:5
(dlv) c
> main.hello() ./main.go:5 (hits goroutine(18):1 total:1) (PC: 0x1330e73)
1: package main
2:
3: import "net/http"
4:
=> 5: func hello(writer http.ResponseWriter, request *http.Request) {
6: host := request.Host
7: writer.Write([]byte(host))
8: }
9:
10: func main() {
(dlv) n
> main.hello() ./main.go:6 (PC: 0x1330e81)
1: package main
2:
3: import "net/http"
4:
5: func hello(writer http.ResponseWriter, request *http.Request) {
=> 6: host := request.Host
7: writer.Write([]byte(host))
8: }
9:
10: func main() {
11: http.HandleFunc("/hello",hello)
(dlv) n
> main.hello() ./main.go:7 (PC: 0x1330ea3)
2:
3: import "net/http"
4:
5: func hello(writer http.ResponseWriter, request *http.Request) {
6: host := request.Host
=> 7: writer.Write([]byte(host))
8: }
9:
10: func main() {
11: http.HandleFunc("/hello",hello)
12: http.ListenAndServe(":8080",nil)
(dlv) args
writer = net/http.ResponseWriter(*net/http.response) 0xc000115968
request = ("*net/http.Request")(0xc000144100)
(dlv) p host
"localhost:8080"
输入 n 回车,执行到下一行
输入s 回车,单步执行
输入 print(别名p)输出变量信息
输入 args 打印出所有的方法参数信息
输入 locals 打印所有的本地变量
二进制文件调试
go build main.go,执行当前./demo 程序
ps -ef|grep demo
501 11215 554 0 12:11上午 ttys001 0:00.01 ./demo
501 11280 11218 0 12:11上午 ttys002 0:00.00 grep demo
dlv attch 11215
(dlv) s
> main.hello() ./go/src/baxiang.cn/demo/main.go:7 (PC: 0x124f620)
Warning: debugging optimized function
2:
3: import "net/http"
4:
5: func hello(writer http.ResponseWriter, request *http.Request) {
6: host := request.Host
=> 7: writer.Write([]byte(host))
8: }
9:
10: func main() {
11: http.HandleFunc("/hello",hello)
12: http.ListenAndServe(":8080",nil)
(dlv) locals
host = "localhost:8080"
go tool
import "fmt"
func main() {
fmt.Println("foo")
return
fmt.Printf("bar")
}
在golang1.12上已经换成了go vet
go tool vet main.go
vet: invoking "go tool vet" directly is unsupported; use "go vet"
下面执行结果表示当前代码行无法被执行的
go vet main.go
# command-line-arguments
./main.go:8:2: unreachable code
分析锁的问题
import (
"sync"
)
type Foo struct {
lock sync.Mutex
}
func (f *Foo) Lock() {
f.lock.Lock()
}
func (f Foo) Unlock() {
f.lock.Unlock()
}
func main() {
f := Foo{sync.Mutex{}}
f.Lock()
f.Unlock()
f.Lock()
}
t.lock.Unlock() 实际上是由 lock 的副本调用的。在锁传值使用了值传递 需要修改,否则出现死锁。
go vet main.go
# command-line-arguments
./main.go:15:9: Unlock passes lock by value: command-line-arguments.Foo