第八章 go单元测试--表格驱动测试, 性能测试

前端之家收集整理的这篇文章主要介绍了第八章 go单元测试--表格驱动测试, 性能测试前端之家小编觉得挺不错的,现在分享给大家,也给大家做个参考。

一. go语言单元测试写法

1. 文件命令,测试内容+ _test

2. 测试的方法名以Test开头. 参数为(test *Test)

3. 测试文件和源文件放在同一个目录中

例:

package TestingDebug

func Add(a,b int) int {
    return b + a
}
package TestingDebug

import (
    "fmt"
    testing"
)

func TestAdd(t *testing.T) {
    var test = []struct{
        a,b,c 
    }{
        {1,2,1)">3},{3,1)">5,1)">84,1)">5for _,tt := range test  {
        if  actural := Add(tt.a,tt.b) ; actural != tt.c {
            fmt.Printf(%d + %d,except:%d,actual:%d,tt.a,tt.b,tt.c,actural)
        }
    }
}

需要注意几点

1. 同目录下文件相互调用,不需要加包名

2. 测试文件与源文件在同一个目录下,  否则技术不出来测试代码覆盖率.

 

二. 如何测试代码覆盖率

 

 

 然后点击TestingDebug进入文件夹,点击测试的文件. 看右侧绿线,表示测试覆盖的代码. 这里覆盖了100%

 三. 性能测试BenchMark

性能测试使用Testing.B

比如,我们想要测试一个复杂一些的数据的累加,看看性能如何. 

/**
 * 性能测试
 */
func BenchmarkAdd(bb *testing.B) {
    var a,c 
    a = 123
    b = 4557
    c = 4680
    for i := 0; i<bb.N ; i++  {
        if  actural := Add(a,b) ; actural != c {
            fmt.Printf(}
}

bb.N表示的是系统自动计算的一个循环次数,我们不用自己指定.

goos: darwin
goarch: amd64
pkg: aaa/TestingDebug
BenchmarkAdd-8       1000000000             0.317 ns/op
PASS

以上是测试结果. 1000000000 代表测试的次数是10亿次. 0.317 ns/op每个操作执行的时间是0.317ns

 四. 性能调优--使用pprof进行性能优化

如上一步,我们对代码进行了性能测试. 每个操作执行时间是0.317ns. 那么,假如有一个稍微复杂一些的例子,我如何看出花费了这么多时间,都是在哪个步骤花的? 哪个步骤耗费时间最多呢?

我们使用命令来看

go test -bench . -cpuprofile cpu.out

执行上面的命令生成一个cpu.out的文件,查看cpu运行的情况

 

 

我们使用less查看文件: less cpu.out

 

 

 cpu.out是一个二进制文件,我们没办法打开. 我们可以使用命令打开

go tool pprof cpu.out

 

 这里我们可以通过pprof的web视图来查看到底那个步骤耗时最多. 但是看红色字,查看web视图,需要安装一个软件Graphviz

安装Graphviz,下载地址: http://www.graphviz.org/. 我是mac,下载一个mac版本的: 

下载命令: brew install graphviz

安装成功以后,数据命令 go tool pprof cpu.out,然后输入web,会生产一个svg文件,用浏览器打开我们就会看到效果

 

这就是对简单程序的一个分析. 他在每个阶段用时是多少. 因为我们这里是很简单的一个demo,所以耗时很少了.  

 

五. 测试http服务器

http测试分为两种

1. 通过使用假的Request/Response实现 : 运行速度更快,可以测试更多种情况,更像单元测试. 

2. 通过启服务器: 模拟的诊室服务,覆盖代码更多.

 

 

1. 使用假的Request和Response模拟http请求实现单元测试 

分析:

首先: 明确我们的目标,要对谁做测试. 我们要对封装的错误处理进行测试. 

第二: 测试有输入和输出. 预计输入输出,真实输入输出. 我们这里输入是谁呢?通过包装类WrapError分析,入参是对http请求业务逻辑的处理,出参是包装的错误处理,包括错误码和错误消息

所以,单元测试的表格的结构体怎么写呢?

test := []{
    h appHandler     // 入参
    Code int         出参
    Message string     出参
} {
    
}

入参是一个appHandler函数. 因此我们定义一个函数,来模拟返回的错误信息. 异常的种类有很多,先定义一个panic

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

接下来模拟http请求,判断异常处理是否正确,完整代码如下:

package main

import (
    io/IoUtilnet/httpnet/http/httpteststrings
)


func handlerPanic(writer http.ResponseWriter,request *)
}

func TestWrapError(t * 我们测试的异常的封装方法 那么对于单元测试来说,无非就是入参和出参
    test := []{
        h appHandler      入参
        Code  出参
        Message     } {
        {handlerPanic,500,1)">Internal Server Error range test {
         要测试的方法WrapError
        f := WrapError(tt.h)
         模拟Request和response
        response := httptest.NewRecorder()
        request := httptest.NewRequest(http.MethodGet,1)">http://www.baidu.com读取reponse返回的body
        b,_ := IoUtil.ReadAll(response.Body)
        body := strings.Trim(string(b),1)">\n)
         测试返回值和预期是否一致
        if tt.Code != response.Code || tt.Message != body {
            t.Errorf(预期返回--code:%d,message:%s \n 实际返回--code:%d,message:%s用户自定义异常,我们在测试用户自定义信息


)
//用户自定义异常
type testUserError string

func (u testUserError) Error() string{
    return u.Message()
}

func (u testUserError) Message() string {
    return string(u)
}

func handlerUserError(writer http.ResponseWriter,1)">http.Request) error {
    return testUserError(user error)
}


func handlerPanic(writer http.ResponseWriter,{handlerUserError,400,"user error"},
    }

    代码覆盖率 35%. 接下来补全测试代码,提高代码覆盖率

github.com/pkg/errorsos
)

type testUserError string

func (u testUserError) Error() {
    return u.Message()
}

func (u testUserError) Message() return (u)
}

func handlerUserError(writer http.ResponseWriter,1)">)
}


func handlerPanicError(writer http.ResponseWriter,1)">)
}

func handlerNotFountError(writer http.ResponseWriter,1)"> os.ErrNotExist
}

func handlerForbiddenError(writer http.ResponseWriter,1)"> os.ErrPermission
}

func otherError(writer http.ResponseWriter,1)">return errors.New(123)
}

func noError(writer http.ResponseWriter,1)"> nil
}

func TestWrapError(t *    } {
        {handlerPanicError,{handlerUserError,1)">400,{handlerNotFountError,1)">404,1)">Not Found403,1)">Forbidden200,1)">""代码覆盖率

 

 

 达到了80%,只有最后的main方法没有被覆盖. ok了

2. 使用真实的http请求进行测试

 真实http请求
func TestWrapErrorForServer(t * WrapError(tt.h)
        
        s := httptest.NewServer(http.HandlerFunc(f))
         这里url不用填自己的,httptest在new server的时候帮我们定义好了一个url
        response,1)"> s.Client().Get(s.URL)

        b,_ :=)

        if tt.Code != response.StatusCode || tt.Message !=except--code: %d,message: %s \n actual--code:%d,response.StatusCode,body)
        }
    }
}

模拟数据的部分,两种类型的http请求是一样的,被提取到外面了,最终完整代码如下:

 nil
}

 测试数据
} {
    {handlerPanicError,}

 模拟http请求
func TestWrapError(t *读取reponse返回的body
        verify(response.Result(),tt,t)
    }
}

 WrapError(tt.h)

        s := s.Client().Get(s.URL)

        verify(response,t)
    }
}

func verify(response *http.Response,tt struct {h appHandler;Code int;Message string},t *testing.T) {
    b,1)"> IoUtil.ReadAll(response.Body)
    body := strings.Trim()
     body {
        t.Errorf(
package fileListener

import (
    
)

type UserError 

func (u UserError) Error()  u.Message()
}

func (u UserError) Message() (u)
}

func FileHandler(writer http.ResponseWriter,1)"> 获取url路径,路径是/list/之后的部分
    if c := strings.Index(request.URL.Path,1)">/list/"); c != 0 {


        return UserError(url 不是已list开头)
    }
    path := request.URL.Path[len():]
     打开文件
    file,err := os.Open(path)
    if err != nil {
         err
    }
    defer file.Close()

     读出文件
    b,1)"> IoUtil.ReadAll(file)
     err
    }

     写入文件页面
    writer.Write(b)
     nil
}
aaa/errorhandling/filelistenerserver/fileListenergithub.com/kelseyhightower/confd/log
)

 定义一个函数类型的结构,返回值是erro
type appHandler func(writer http.ResponseWriter,1)">http.Request) error

 封装error
func WrapError(handler appHandler) func (http.ResponseWriter,*http.Request) {
    return func(writer http.ResponseWriter,1)">http.Request) {
        defer func(){
            if r := recover(); r != nil {
                log.Info(发生异常)
                http.Error(writer,http.StatusText(http.StatusInternalServerError),http.StatusInternalServerError)
            }
        }()

         执行原来的逻辑. 然后增加error的错误处理
        err := handler(writer,1)"> nil {
            if userErr,ok := err.(userError); ok {
                http.Error(writer,userErr.Message(),http.StatusBadRequest)
                
            }
            code := http.StatusOK
            switch {
            case os.IsNotExist(err):
                code = http.StatusNotFound

             os.IsPermission(err):
                code = http.StatusForbidden
            default:
                code = http.StatusInternalServerError
            }
            http.Error(writer,http.StatusText(code),code)
        }
    }
}

type userError interface {
    error         系统异常
    Message()  用户自定义异常
}
 我们来模拟一个web服务器. 在url上输入一个地址,然后显示文件内容
 做一个显示文件的web server
func main() {
    http.HandleFunc(/ 监听端口:8888
    err := http.ListenAndServe(:8888if  err != nil {
        panic(err)
    }
}

代码结构

 

猜你在找的Go相关文章