golang 單元測試和性能測試

單元測試

  • 單元測試(Unit Tests, UT) 是一個優(yōu)秀項目不可或缺的一部分,特別是在一些頻繁變動和多人合作開發(fā)的項目中尤為重要。你或多或少都會有因?yàn)樽约旱奶峤唬瑢?dǎo)致應(yīng)用掛掉或服務(wù)宕機(jī)的經(jīng)歷。比如我就曾經(jīng)就因?yàn)榇蛴×藄ql,而忘記關(guān)閉,導(dǎo)致所有的接口異常(允悲)。還有就是如果你優(yōu)化了一些代碼邏輯,優(yōu)化了某個函數(shù)。那么我們怎么保證我們的正確性呢?那么幾個測試用例或許就可以解決這個問題。

測試文件的命名

  • go語言是支持單元測試的,測試文件是_test.go結(jié)尾的文件名

測試用例的命名

  • 測試用例名稱一般命名為 Test 加上待測試的方法名。比如TestAdd(),測試用的參數(shù)有且只有一個,在這里是 t *testing.T

簡單入門

  • 我們來寫一個Add函數(shù),創(chuàng)建文件add.go
package main

import "fmt"

func main() {
    sum := Add(1, 2)
    fmt.Println(sum)
}

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

  • 我們在add.go目錄下創(chuàng)建一個add_test.go的測試文件
package main

import (
    "testing"
)

func TestAdd(t *testing.T) {
    testData := []struct {
        a int
        b int
        c int
    }{
        {1, 2, 3},
        {4, 5, 9},
        {50, 5, 5},
    }
    ans := 0
    for _, data := range testData {
        if ans = Add(data.a, data.b); ans != data.c {
            t.Errorf("%d + %d expected %d,but %d got", data.a, data.b, data.c, ans)
        }
    }

}

我們先來認(rèn)識一下ide調(diào)試工具,當(dāng)然這些調(diào)試我們在命令也是可以運(yùn)行的

  • Run 執(zhí)行腳本
  • Debug 開啟Debug模式
  • coverprofile 代碼覆蓋率
  • CPU Profiler cup分析
  • Memory Profiler 內(nèi)存分析 對象、堆的分析和內(nèi)存泄漏等
  • Blocking Profiler 記錄 goroutine的阻塞情況,等待和同步情況,timer/channel通訊等各項細(xì)節(jié)
  • Mutex Profiler 互斥鎖分析,包括各種競爭情況


    在這里插入圖片描述
  • 我通過ide運(yùn)行一下


    在這里插入圖片描述
  • 我們也可以使用ide的debug功能打斷點(diǎn)調(diào)試


    在這里插入圖片描述
  • 命令行測試,首先我們進(jìn)入add.go目錄


    在這里插入圖片描述

代碼覆蓋率

  • IDE調(diào)試


    在這里插入圖片描述
  • 我們使用命令行再試一下
zhangguofu@zhangguofudeMacBook-Pro add (master) $ go test -coverprofile=c.out 
--- FAIL: TestAdd (0.00s)
    add_test.go:20: 50 + 5 expected 5,but 55 got
FAIL
coverage: 33.3% of statements
exit status 1
FAIL    goapp/src/learngo/add   0.502s
zhangguofu@zhangguofudeMacBook-Pro add (master) $ ls
add.go      add_test.go c.out
zhangguofu@zhangguofudeMacBook-Pro add (master) $ cat c.out
mode: set
goapp/src/learngo/add/add.go:5.13,8.2 2 0
goapp/src/learngo/add/add.go:10.28,12.2 1 1


我們發(fā)現(xiàn)多了一個c.out 文件,但是里面的內(nèi)容我們看不太懂,我們借助工具go tool cover ,查看代碼覆蓋的相關(guān)命令幫助


在這里插入圖片描述

我們使用` go tool cover -html=c.out``,此時會打開一個html頁面,上面標(biāo)注了我們代碼覆蓋的情況


在這里插入圖片描述

性能測試

  • 我們通過一個獲取最長不重復(fù)字符串函數(shù)來測試一下性能
  • 函數(shù)代碼 在add.go文件中
/**
最大字符串不重復(fù)
*/
func Repeat(s string) int {
    //保持最大不重復(fù)串
    lastOccur := make(map[string]int)
    start := 0
    max := 0
    for k, v := range []rune(s) {
        if index, ok := lastOccur[string(v)]; ok && start <= index {
            start = index + 1
        }
        if max < k-start+1 {
            max = k - start + 1
        }
        lastOccur[string(v)] = k
    }
    return max
}
  • 性能測試代碼 在add_test.go文件中
func BenchmarkRepeat(b *testing.B) {
    str := "不經(jīng)歷風(fēng)雨,怎么見彩虹?沒有人能隨隨便便成功"
    data := struct {
        content string
        res     int
    }{
        str,
        17,
    }
    for i := 0; i < b.N; i++ {
        if len := Repeat(data.content); len != 17 {
            b.Error("the program is wrong")
        }
    }
}
在這里插入圖片描述
  • 結(jié)果是3ms左右,我們加長一點(diǎn)看看
func BenchmarkRepeat(b *testing.B) {
    str := "不經(jīng)歷風(fēng)雨,怎么見彩虹?沒有人能隨隨便便"

    for i := 0; i < 20; i++ {
        str += str
    }

    b.Logf("the len of str is %d", len(str))
    b.ResetTimer() //忽略上面的計算時間
    for i := 0; i < b.N; i++ {
        Repeat(str)
    }
}
  • 我們發(fā)現(xiàn)對于計算6m文件中的不重復(fù)字符串,程序執(zhí)行了大概是16s左右,這個感覺很慢了,對不對,但是我們想看程序是時間花在哪里了


    在這里插入圖片描述
  • 和代碼覆蓋率的命令類似。我執(zhí)行命令
zhangguofu@zhangguofudeMacBook-Pro add (master) $ go test -bench . -cpuprofile  cpu.out
goos: darwin
goarch: amd64
pkg: goapp/src/learngo/add
BenchmarkRepeat-8              1        1541179761 ns/op
--- BENCH: BenchmarkRepeat-8
    add_test.go:33: the len of str is 62914560
PASS
ok      goapp/src/learngo/add   2.077s
zhangguofu@zhangguofudeMacBook-Pro add (master) $ ls
add.go      add.test    add_test.go c.out       cpu.out
zhangguofu@zhangguofudeMacBook-Pro add (master) $ less cpu.out  
"cpu.out" may be a binary file.  See it anyway? 

發(fā)現(xiàn)生成的cpu.out 是一個二進(jìn)制文件,我們使用go tool pprof cpu.out 查看,進(jìn)入交互模式

在這里插入圖片描述

我輸入web格式的,報錯說需要安裝Graphviz ,那就安裝吧 下載地址 如果安裝失敗 可以參考我這篇文章解決brew 安裝軟件失敗的問題

  • 再次輸入go tool pprof cpu.out 輸入web
    沒有報錯了,但是此處我碰到一個個問題。就是我的虛擬機(jī)(mac上面裝的win10)會默認(rèn)打開svg 文件,很煩人
    解決方式
    在這里插入圖片描述

    并且設(shè)置谷歌瀏覽器為默認(rèn)打開方式
在這里插入圖片描述
  • 最終,我們打開了這個文件,這里面箭頭越粗,代表花費(fèi)的時間越長,我們可以根據(jù)結(jié)果 對我們的程序進(jìn)行優(yōu)化,比如在這里,我們看到是string encode decode 很浪費(fèi)時間,那么我們是不是可以選擇更合適的數(shù)據(jù)類型來存儲我們的數(shù)據(jù)呢,比如我們存的是 abc我愛中國 ,那么再計算中,來回轉(zhuǎn)換是很麻煩的,我們是不是可以轉(zhuǎn)換為int32來計算呢。
    在這里插入圖片描述
  • golang的單元測試和性能測試就先到這里了。喜歡可以點(diǎn)贊哦?。?/li>
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容