Go单元测试实例

单元测试

Go语言为我们提供了测试框架,以便帮助我们更容易的进行单元测试,但是要使用这个框架,需要遵循如下几点规则:
1、含有单元测试代码的go文件必须以_test.go结尾,Go语言测试工具只认符合这个规则的文件
2、单元测试文件名_test.go前面的部分最好是被测试的方法所在go文件的文件名,比如例子中是wxidcmdqueue_test.go。
3、单元测试的函数名必须以Test开头,是可导出公开的函数。
4、测试函数的签名必须接收一个指向testing.T类型的指针,并且不能返回任何值。
5、函数名最好是Test+要测试的方法函数名,比如例子中是TestPushCmdQueue,表示测试的是PushCmdQueue这个这个函数。

虽然可以用 fmt.printf etc打印信息, 但无法进行log信息与测试内容的分类控制(-v 无效)
强烈建议用

t.Log t.Logf        // 正常信息 
t.Error t.Errorf    // 测试失败信息 
t.Fatal t.Fatalf    // 致命错误, 测试程序退出的信息

项目源代码wxidcmdqueue.go


//GetWxidCmdQueueInstance 获取指令队列对象
func GetWxidCmdQueueInstance() *WxidCmdQueue {
    once.Do(func() {
        instance = &WxidCmdQueue{
            wxidcmdqueue: &sync.Map{},
        }
    })

    return instance
}

//PushCmdQueue 加入指令
func (wq *WxidCmdQueue) PushCmdQueue(wxid string, data []byte) {
    var cmd MatrixCmd

    jsondata, err := simplejson.NewJson(data)
    if err != nil {
        // mlog.Error("cmd content not json")
        return
    }
    cmd.CmdContent = data
    cmd.Priority = jsondata.Get("cmd_type").MustInt(0)
    queue, ok := wq.wxidcmdqueue.Load(wxid)
    if ok {
        queue.(*MatrixCmdHeap).Push(&cmd)
    } else {
        mcheap := &MatrixCmdHeap{}
        mcheap.Push(&cmd)
        wq.wxidcmdqueue.Store(wxid, mcheap)
    }
}

//PopCmdQueue 弹出指令
func (wq *WxidCmdQueue) PopCmdQueue(wxid string) interface{} {
    queue, ok := wq.wxidcmdqueue.Load(wxid)
    if ok {
        ret := queue.(*MatrixCmdHeap).Pop()
        if ret != nil {
            return ret.(*MatrixCmd).CmdContent
        }
    }
    return nil
}

//GetCmdQueueLen 获取wxid对应的指令队列长度
func (wq *WxidCmdQueue) GetCmdQueueLen(wxid string) int {
    queue, ok := wq.wxidcmdqueue.Load(wxid)
    if ok {
        return queue.(*MatrixCmdHeap).GetLength()
    }
    return 0
}

单元测试文件以_test.go结尾。
测试文件 wxidcmdqueue_test.go


func formatCmd(wxid string, cmdtype int) []byte {
    json := simplejson.New()
    json.Set("wx_id", wxid)
    json.Set("cmd_type", cmdtype)
    ret, err := json.Encode()
    if err != nil {
        return nil
    }
    return ret
}

func TestPushCmdQueue(t *testing.T) {
    mCmdQueue := GetWxidCmdQueueInstance()
    for cmdtype := 20010; cmdtype > 20000; cmdtype-- {
        cmd := formatCmd("wxid", cmdtype)
        mCmdQueue.PushCmdQueue("wxid", cmd)
    }

    if mCmdQueue.GetCmdQueueLen("wxid") != 10 {
        t.Error("cmdqueue len error")
    }

}

func TestPopCmdQueue(t *testing.T) {
    mCmdQueue := GetWxidCmdQueueInstance()
    mCmdQueue.CleanCmdQueueLen("wxid")
    for cmdtype := 20010; cmdtype > 20000; cmdtype-- {
        cmd := formatCmd("wxid", cmdtype)
        mCmdQueue.PushCmdQueue("wxid", cmd)
    }

    for ctype := 20001; ctype <= 20010; ctype++ {
        mCmd := mCmdQueue.PopCmdQueue("wxid")
        mCmdContent, err := simplejson.NewJson(mCmd.([]byte))
        if err != nil {
            t.Error("cmd not json")
        } else {
            cmdtype := mCmdContent.Get("cmd_type").MustInt(0)
            if cmdtype != ctype {
                t.Error("cmd type error", cmdtype, "---", ctype)
            }
        }
    }
}

go test -run TestPushCmdQueue 运行指定的测试函数

e:\Go_workspace\src\matrix\cmdqueue>go test -v -run TestPushCmdQueue
=== RUN   TestPushCmdQueue
--- PASS: TestPushCmdQueue (0.00s)
PASS
ok      matrix/cmdqueue 0.017s

go test -v 运行整个测试文件

e:\Go_workspace\src\matrix\cmdqueue>go test  -v
=== RUN   TestPushCmdQueue
--- PASS: TestPushCmdQueue (0.00s)
=== RUN   TestPopCmdQueue
--- PASS: TestPopCmdQueue (0.00s)
PASS
ok      matrix/cmdqueue 0.017s

如果其中存在错误
1、向指令优先级队列插入10个指令,判断写成11个
2、弹出队列时如果指令号对应不上

e:\Go_workspace\src\matrix\cmdqueue>go test  -v
=== RUN   TestPushCmdQueue
--- FAIL: TestPushCmdQueue (0.00s)
        wxidcmdqueue_test.go:29: cmdqueue len error
=== RUN   TestPopCmdQueue
--- FAIL: TestPopCmdQueue (0.00s)
        wxidcmdqueue_test.go:55: cmd type error 20001 --- 20001
        wxidcmdqueue_test.go:55: cmd type error 20002 --- 20002
        wxidcmdqueue_test.go:55: cmd type error 20003 --- 20003
        wxidcmdqueue_test.go:55: cmd type error 20004 --- 20004
        wxidcmdqueue_test.go:55: cmd type error 20005 --- 20005
        wxidcmdqueue_test.go:55: cmd type error 20006 --- 20006
        wxidcmdqueue_test.go:55: cmd type error 20007 --- 20007
        wxidcmdqueue_test.go:55: cmd type error 20008 --- 20008
        wxidcmdqueue_test.go:55: cmd type error 20009 --- 20009
        wxidcmdqueue_test.go:55: cmd type error 20010 --- 20010
FAIL
exit status 1
FAIL    matrix/cmdqueue 0.016s
项目工程下的单元测试

将项目下的所有包进行单元测试

E:\Go_workspace\src\matrix>go test matrix/cmdqueue matrix/datamanager
ok      matrix/cmdqueue (cached)
ok      matrix/datamanager      0.612s

测试覆盖率

测试覆盖率要多加一个参数-coverprofile,完整的命令为:go test -v -coverprofile=c.out,-coverprofile是指定生成的覆盖率文件。

e:\Go_workspace\src\matrix\cmdqueue>go test -v -run TestPushCmdQueue  -coverprofile=c.out
=== RUN   TestPushCmdQueue
--- PASS: TestPushCmdQueue (0.00s)
PASS
coverage: 63.0% of statements
ok      matrix/cmdqueue 0.018s


e:\Go_workspace\src\matrix\cmdqueue>go test  -v -coverprofile=c.out
=== RUN   TestPushCmdQueue
--- PASS: TestPushCmdQueue (0.00s)
=== RUN   TestPopCmdQueue
--- PASS: TestPopCmdQueue (0.00s)
PASS
coverage: 94.4% of statements
ok      matrix/cmdqueue 0.015s

生成HTML测试覆盖率文件

go tool cover -html=c.out -o coverage.html

基准测试

基准测试主要是通过测试CPU和内存的效率问题,来评估被测试代码的性能,进而找到更好的解决方案。比如链接池的数量不是越多越好,那么哪个值才是最优值呢,这就需要配合基准测试不断调优了。基准测试的规则:
1、基准测试的代码文件必须以_test.go结尾。
2、基准测试的函数必须以Benchmark开头,必须是可导出的。
3、基准测试函数必须接受一个指向Benchmark类型的指针作为唯一参数。
4、基准测试函数不能有返回值。
5、b.ResetTimer是重置计时器,这样可以避免for循环之前的初始化代码的干扰。最后的for循环很重要,被测试的代码要放到循环里。
6、b.N是基准测试框架提供的,表示循环的次数,因为需要反复调用测试的代码,才可以评估性能。\

有些测试需要一定的启动和初始化时间,如果从Benchmark()函数开始计时会很大程度上影响测试结果的精准性。testing.B提供了一系列的方法可以方便地控制计时器,从而让计时器只在需要的区间进行测试。我们通过下面的代码来了解计时器的控制。

// 重置计时器
b.ResetTimer()
// 停止计时器
b.StopTimer()
// 开始计时器
b.StartTimer()

func BenchmarkPushCmdQueue(b *testing.B) {
    mCmdQueue := GetWxidCmdQueueInstance()
    b.ResetTimer()
    for wxidnum := 0; wxidnum < b.N; wxidnum++ {
        wxid := fmt.Sprintf("wxid_%d", wxidnum)
        for cmdtype := 20010; cmdtype > 20000; cmdtype-- {
            cmd := formatCmd(wxid, cmdtype)
            mCmdQueue.PushCmdQueue(wxid, cmd)
        }
    }
}

func BenchmarkPopCmdQueue(b *testing.B) {
    mCmdQueue := GetWxidCmdQueueInstance()
    for wxidnum := 0; wxidnum < b.N; wxidnum++ {
        wxid := fmt.Sprintf("wxid_%d", wxidnum)
        for cmdtype := 20010; cmdtype > 20000; cmdtype-- {
            cmd := formatCmd(wxid, cmdtype)
            mCmdQueue.PushCmdQueue(wxid, cmd)
        }
    }
    b.ResetTimer()
    for wxidnum := 0; wxidnum < b.N; wxidnum++ {
        wxid := fmt.Sprintf("wxid_%d", wxidnum)
        for cmdtype := 20010; cmdtype > 20000; cmdtype-- {
            mCmdQueue.PopCmdQueue(wxid)
        }
    }
}

测试结果

e:\Go_workspace\src\matrix\cmdqueue>go test -bench=. -run=none
goos: windows
goarch: amd64
pkg: matrix/cmdqueue
BenchmarkPushCmdQueue-8            30000             45200 ns/op
BenchmarkPopCmdQueue-8            500000              2044 ns/op
PASS
ok      matrix/cmdqueue 27.442s

函数后面的-8表示运行时对应的GOMAXPROCS的值。
接着的30000/500000和表示运行for循环的次数,也就是调用被测试代码的次数。
最后的45200 ns/op和2044 ns/op表示每次需要花费的时间。
还可以通过-benchtime参数可以自定义测试时间。默认是1秒,也就是1秒的时间,调用30000/500000次,每次调用花费45200/2044纳秒。如果想让测试运行的时间更长,可以通过-benchtime指定,比如2秒。可以发现,我们加长了测试时间,测试的次数变多了,但是最终的性能结果:每次执行的时间,并没有太大变化,意义不大,所以使用默认值就好。

e:\Go_workspace\src\matrix\cmdqueue>go test -bench=. -benchtime=2s -run=none
goos: windows
goarch: amd64
pkg: matrix/cmdqueue
BenchmarkPushCmdQueue-8           100000             45690 ns/op
BenchmarkPopCmdQueue-8           1000000              2186 ns/op
PASS
ok      matrix/cmdqueue 60.194s
内存

-benchmem可以提供每次操作分配内存的次数,以及每次操作分配的字节数。

e:\Go_workspace\src\matrix\cmdqueue>go test -bench=. -run=none -benchmem
goos: windows
goarch: amd64
pkg: matrix/cmdqueue
BenchmarkPushCmdQueue-8            30000             44566 ns/op           26554 B/op        329 allocs/op
BenchmarkPopCmdQueue-8           1000000              2099 ns/op              23 B/op          2 allocs/op
PASS
ok      matrix/cmdqueue 100.010s

26554 B/op 代表每次操作分配26554B的内存大小。
329 allocs/op 代表每次操作进行329次内存分配。

CPU

cpuprofile是表示生成的cpu profile文件

e:\Go_workspace\src\matrix\cmdqueue>go test -bench=".*" -cpuprofile=cpu.prof ./
goos: windows
goarch: amd64
pkg: matrix/cmdqueue
BenchmarkPushCmdQueue-8            30000             48366 ns/op
BenchmarkPopCmdQueue-8            500000              2024 ns/op
PASS
ok      matrix/cmdqueue 26.987s
:\Go_workspace\src\matrix\cmdqueue>go tool pprof cmdqueue.test.exe cpu.prof
File: cmdqueue.test.exe
Type: cpu
Time: Nov 15, 2018 at 5:20pm (CST)
Duration: 26.64s, Total samples = 40.19s (150.86%)
Entering interactive mode (type "help" for commands, "o" for options)
(pprof) top
Showing nodes accounting for 24110ms, 59.99% of 40190ms total
Dropped 184 nodes (cum <= 200.95ms)
Showing top 10 nodes out of 114
      flat  flat%   sum%        cum   cum%
    9020ms 22.44% 22.44%    15990ms 39.79%  runtime.scanobject
    4510ms 11.22% 33.67%     4510ms 11.22%  runtime.heapBitsForObject
    3060ms  7.61% 41.28%     4580ms 11.40%  runtime.mallocgc
    2530ms  6.30% 47.57%     2550ms  6.34%  runtime.greyobject
    1110ms  2.76% 50.34%     1110ms  2.76%  runtime.memclrNoHeapPointers
    1080ms  2.69% 53.02%     1080ms  2.69%  runtime.heapBitsSetType
    1010ms  2.51% 55.54%     1670ms  4.16%  runtime.mapaccess2
     640ms  1.59% 57.13%     1080ms  2.69%  runtime.mapassign_faststr
     600ms  1.49% 58.62%      600ms  1.49%  runtime.aeshashbody
     550ms  1.37% 59.99%     1760ms  4.38%  encoding/json.(*Decoder).readValue
(pprof)
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值