Go语言学习笔记8——测试与性能调优

Go语言学习笔记8——测试与性能调优

Debugging Sucks! Testing Rocks!

传统测试

在这里插入图片描述

  • 测试逻辑和测试数据混在一起
  • 出错信息不明确
  • 一旦一个数据出错测试全部结束

表格驱动测试

在这里插入图片描述

  • 分离的测试数据和测试逻辑
  • 明确的出错信息
  • 可以部分失败
  • go语言的语法使得我们更易实践表格驱动测试

func calcTriangle(a, b int) int {
    
    
	var c int
	c = int(math.Sqrt(float64(a*a + b*b)))
	return c
}

func TestTriangle(t *testing.T) {
    
    
	tests := []struct{
    
     a, b, c int }{
    
    
		{
    
    3, 4, 5},
		{
    
    5, 12, 13},
		{
    
    8, 15, 17},
		{
    
    12, 35, 37},
		{
    
    30000, 40000, 50000},
	}

	for _, tt := range tests {
    
    
		if actual := calcTriangle(tt.a, tt.b); actual != tt.c {
    
    
			t.Errorf("calcTriangle(%d,%d);"+
				"got %d;expected %d",
				tt.a, tt.b, actual, tt.c)
		}
	}
}

代码覆盖率

查看测试代码的代码覆盖率
绿色的是覆盖到的,红色是没有覆盖到的
在这里插入图片描述
在这里插入图片描述

性能测试


func BenchmarkSubstr(b *testing.B) {
    
    
	aa := 30000
	bb := 40000
	cc := 50000

	for i := 0; i < b.N; i++ {
    
    
		actual := calcTriangle(aa, bb)
		if actual != cc {
    
    
			b.Errorf("calcTriangle(%d,%d);"+
				"got %d;expected %d",
				aa, bb, actual, cc)
		}
	}
}

结果:在这里插入图片描述

pprof性能调优

命令行命令:

  1. go test -bench . -cpuprofile cpu.out 获得cpu性能的日志文件cpu.out
  2. go tool pprof cpu.out 查看日志文件
  3. web 可视化日志文件,需要先安装Graphviz,链接放在下面直接下载安装即可。
    Graphviz

可视化结果:

在这里插入图片描述

优化map的方法

空间换时间
a:=make([]int,0xffff)
假设中文字符最大就是0xFFFF,这里开一个0xFFFF大小的数组可以存储所有字符
使用:a[‘e’]=1(实质:a[0x65]=1);a[‘课’]=1(实质:a[0x8BFE]=1)

性能调优的步骤

  1. -cpuprofile:获取性能数据
  2. go tool pprof:查看性能数据
  3. 分析慢在哪里
  4. 优化代码

http测试

两种方法

  • 通过使用假的Request/Response
  • 通过起服务器

测试的函数参考之前笔记7中的服务器统一错误处理


func errPanic(writer http.ResponseWriter,
	request *http.Request) error {
    
    
	panic(123)
}

type testingUserError string

func (e testingUserError) Error() string {
    
    
	return e.Message()
}
func (e testingUserError) Message() string {
    
    
	return string(e)
}

func errUserError(writer http.ResponseWriter,
	request *http.Request) error {
    
    
	return testingUserError("user error")
}

func errNotFound(writer http.ResponseWriter,
	request *http.Request) error {
    
    
	return os.ErrNotExist
}
func errNotPermission(writer http.ResponseWriter,
	request *http.Request) error {
    
    
	return os.ErrPermission
}
func errUnknown(writer http.ResponseWriter,
	request *http.Request) error {
    
    
	return errors.New("unknown error")
}
func noError(writer http.ResponseWriter,
	request *http.Request) error {
    
    
	fmt.Fprintln(writer, "no error")
	return nil
}
var tests = []struct {
    
    
	h       appHandler
	code    int
	message string
}{
    
    
	{
    
    errPanic, 500, "Internal Server Error"},
	{
    
    errUserError, 400, "user error"},
	{
    
    errNotFound, 404, "Not Found"},
	{
    
    errNotPermission, 403, "Forbidden"},
	{
    
    errUnknown, 500, "Internal Server Error"},
	{
    
    noError, 200, "no error"},
}

//用假的request,response,速度快
func TestErrWrapper(t *testing.T) {
    
    
	for _, tt := range tests {
    
    
		f := errWrapper(tt.h)
		response := httptest.NewRecorder()
		request := httptest.NewRequest(
			http.MethodGet, "http://www.imooc.com", nil)
		f(response, request)
		verifyResponse(response.Result(), tt.code, tt.message, t)
	}
}

//真正起一个server,测试力度更大
func TestErrWrapperInServer(t *testing.T) {
    
    
	for _, tt := range tests {
    
    
		f := errWrapper(tt.h)
		server := httptest.NewServer(http.HandlerFunc(f))
		resp, _ := http.Get(server.URL)
		verifyResponse(resp, tt.code, tt.message, t)
	}
}

func verifyResponse(resp *http.Response, expectedCode int, expectMsg string, t *testing.T) {
    
    
	b, _ := ioutil.ReadAll(resp.Body)
	body := strings.Trim(string(b), "\n")
	if resp.StatusCode != expectedCode || body != expectMsg {
    
    
		t.Errorf("expected(%d,%s);"+
			"got(%d,%s)",
			expectedCode, expectMsg,
			resp.StatusCode, body)
	}
}

生成文档

用注释写文档
在测试中加入Example
使用go doc/godoc 来查看/生成文档

godoc -http :6060生成文档,通过 http://localhost:6060/pkg/ 访问

注释


// An FIFO queue.
type Queue []int

// Pushes the element into the queue.
//  e.g. q.Push(123)
func (q *Queue) Push(v int) {
    
    
	*q = append(*q, v)
}

// Pops element from head.
func (q *Queue) Pop() int {
    
    
	head := (*q)[0]
	*q = (*q)[1:]
	return head
}

// Returns if the queue is empty or not.
func (q *Queue) IsEmpty() bool {
    
    
	return len(*q)==0
}

示例代码:
也可以当做测试来做

func ExampleQueue_Pop() {
    
    
   q := Queue{
    
    1}
   q.Push(2)
   q.Push(3)
   
   fmt.Println(q.Pop())
   fmt.Println(q.Pop())
   fmt.Println(q.IsEmpty())
   fmt.Println(q.Pop())
   fmt.Println(q.IsEmpty())

   // Output:
   // 1
   // 2
   // false
   // 3
   // true
}

生成文档:
在这里插入图片描述

猜你喜欢

转载自blog.csdn.net/shn111/article/details/122677040