8、go语言:测试与性能调优

1、测试:

程序应该多做测试,少做调试

传统测试 VS 表格驱动测试(go语言使用)

//传统测试
@Test public void testAdd(){
	assertEquals(3,add(1,2));
}

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

//表格驱动测试
test := []struct{
	a,b,c int32
}{
	{1,2,3},
	{0,2,2},
	{math.MaxInt32,1,math.MinInt32}, //整数溢出 最大的整数+1成为最小整数
}

for _,test := range tests {
	if actual := add(test.a,test.b);actual != test.c {
	//
	}
}

testing.T的使用
运行测试

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

func trangle(){
	var a,b int = 3,4
	fmt.Println(calcTriangle(a,b))
}

-------------------new test go file--------------------------

package main

import "testing"

func TestTriangle(t *testing.T){
	tests := []struct {a,b,c int} {
		{3,4,5},
		{5,12,13},
		{8,15,17},
	}
	
	for _,tt := range tests{
		if actual := calcTriangle(tt.a,tt.b); actual != tt.c {
			t.Errorf("calcTriangle(%d,%d);" + 
			"got %d;expectd %d",tt.a,tt.b,actual,tt.c) //自定义出错信息
		}
	}
}

用命令行运行
进入项目目录,go test .

//最长不重复子串测试
func TestSubstr(t *testing.T){
	tests := []struct{
			s string
			ans int
		}{
			//Normal case
			{"abcabcbb",3},
			{"pwkkew",3},
			
			//Edge case
			{"",0},
			{"b",1},
			{"abcabcabcd",4},
			
			//chinese support
			{"这里是",3},
		}
	
	for _,tt := range tests{
		actual := lengthOfNonRepeatingSubStr(tt.s)
		if actual != tt.ans{
			t.Errorf("got %d for input %s" + "expected %d",actual,tt.s,tt.ans)
		}
	}
}

2、代码覆盖率和性能测试:

命令行运行
go test -coverprofile=c.out

或者go tool cover -html=c.out

//性能测试
func BenchmarkSubStr(b *testing.B){
	s,ans := "这里是",3
	
	//循环多少遍不用关心,用b.N就行
	for i :0;i<b.N;i++{
		actual := lengthOfNonRepeatingSubStr(tt.s)
		if actual != tt.ans{
			t.Errorf("got %d for input %s" + "expected %d",actual,s,ans)
		}
	}
}

如果用命令行:go test -bench .

3、使用pprof进行性能调优:

//性能测试
func BenchmarkSubStr(b *testing.B){
	s,ans := "这里是",3
	
	//加长字符串进行测试
	for i:=0;i<13;i++{
		s = s + s
	}
	
	b.Logf("len(s) = %d",len(s)) //记log
	
	//循环多少遍不用关心,用b.N就行
	for i :0;i<b.N;i++{
		actual := lengthOfNonRepeatingSubStr(tt.s)
		if actual != tt.ans{
			t.Errorf("got %d for input %s" + "expected %d",actual,s,ans)
		}
	}
}

寻找最长不重复字串国际版:

func lengthOfNonRepatingSubstr(s string) int{
	lastOccurred := make(map[rune] int)
	start := 0
	maxLength := 0
	for i,ch := range []rune(s) {  //utf-8是变长的编码,每一个字节需要decode
		if lastI,ok := lastOccurred[ch] ;ok  && lastI >= start{ //条件语句放一行
			start = lastI + 1
		} 
		if i - start +1 > maxLength {
			maxLength =  i - start +1 
		}
		lastOccurred[ch] =i
	}
	return maxLength
}

先进行测试:
go test -bench . -cpuprofile cpu.out
go tool pprof cpu.out
交互式命令行:web,为了进行图形化显示,还需要安装Graphviz
性能主要在[]rune(s)和map上,[]rune(s)是必须要的,只能优化map,用其他数据结构

func lengthOfNonRepatingSubstr(s string) int{
	//s:="yes,我爱慕课网"
	//lastOccurred := make(map[rune] int)
	//用空间换时间
	lastOccurred := make([]int,0xffff) //开一个大的slice 65535  ,65k,如果移到函数外面,只要make一次
	//初始化
	for i := range lastOccurred {
		lastOccurred[i] = -1
	}
	//lastOccurred[0x65] = 1 //e
	//lastOccurred[0x8BFE] = 6 //课
	
	start := 0
	maxLength := 0
	for i,ch := range []rune(s) {  //utf-8是变长的编码,每一个字节需要decode
		//if lastI,ok := lastOccurred[ch] ;ok  && lastI >= start{ //条件语句放一行
		if lastI := lastOccurred[ch] ;lastI != -1  && lastI >= start{ //条件语句放一行
			start = lastI + 1
		} 
		if i - start +1 > maxLength {
			maxLength =  i - start +1 
		}
		lastOccurred[ch] =i
	}
	return maxLength
}

-cpuprofile获取性能数据
go tool pprof查看性能数据
分析慢在哪里
优化代码

4、测试http服务器(上):

测试errwrapper

func TestWrapper(t *testing.T){
	//httptest.NewRecorder()
	tests := struct{
			h appHander
			code int
			message string
		}{
			{errPanic,500,"Internal Server Error"},
		}
	
	for _,tt := range tests {
		f := errWrapper(tt.h)
		response := httptest.Newrecoder()
		request := httptest.NewRequest(
		http.MethodGet,
		//"",nil)不能为空
		"http://jessiejacob.com",nil)
		f(response,request)
		
		//下一节的替换
		verityResponse(response.Result(),tt,code,tt.message,t) //newRecoder的提取
		
		b,_ := ioutil.ReadAll(response.Body)
		//body := string(b) //转换成字符串
		body := strings.Trim(string(b),"\n") //转换成字符串,去除换行
		if response.Code != tt.code ||
			body != tt.message {
				t.Errorf("ecpect (%d,%s);" + " got(%d,%s)",tt.code,tt.message,reponse.Code,body)
			}
	}
}

5、测试http服务器(下):

//提出公用
var tests := struct{
		h appHander
		code int
		message string
	}{
		{errPanic,500,"Internal Server Error"},
	}

//提取方法
funv verityResponse(resp *http.Response,expectedCode int,expectedMsg string,t *testing.T){
	b,_ := ioutil.ReadAll(resp.Body)
	//body := string(b) //转换成字符串
	body := strings.Trim(string(b),"\n") //转换成字符串,去除换行
	if resp.StatusCode != expectedCode ||
		body != expectedMsg {
			t.Errorf("ecpect (%d,%s);" + " got(%d,%s)",tt.code,expectedMsg,resp.StatusCode,body)
		}
}

func TestErrWrapperInServer(t *testing.T){
	for _,tt := range tests {
		f := errWrapper(tt.h)
		httptest.NewServer(http.HandlerFunc(f)) //专程server interface
		//
		resp,_ := http.Get(server.URL)
		
		//调用函数
		verityResponse(resp,tt,code,tt.message,t)
	
	}
}

通过使用假的Request/Response (类似于单元测试)
通过起服务器(缺点是慢)

6、生成文档和示例代码:

go doc
go doc Queue
go help doc
godoc -http :6060 //特别有用,直接在本地查看函数文档

-----------以queue示例------------------------------------

package queue

//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.IsEmpty()) //false
	
	//写示例代码
	//Output:
	//false
}

//自动生成code和output

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

7、测试总结:

表格驱动测试
代码覆盖
性能优化工具
http测试
文档以及示例代码

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值