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测试
文档以及示例代码