一区二区三区在线-一区二区三区亚洲视频-一区二区三区亚洲-一区二区三区午夜-一区二区三区四区在线视频-一区二区三区四区在线免费观看

腳本之家,腳本語言編程技術(shù)及教程分享平臺(tái)!
分類導(dǎo)航

Python|VBS|Ruby|Lua|perl|VBA|Golang|PowerShell|Erlang|autoit|Dos|bat|

服務(wù)器之家 - 腳本之家 - Golang - Go語言單元測試超詳細(xì)解析

Go語言單元測試超詳細(xì)解析

2022-02-25 23:55酷爾。 Golang

本文介紹了了Go語言單元測試超詳細(xì)解析,測試函數(shù)分為函數(shù)的基本測試、函數(shù)的組測試、函數(shù)的子測試,進(jìn)行基準(zhǔn)測試時(shí)往往是對(duì)函數(shù)的算法進(jìn)行測驗(yàn),有時(shí)后一個(gè)算法在測試數(shù)據(jù)的基量不同時(shí)測試出的效果會(huì)不同我們需要對(duì)不同數(shù)量

前言:

平時(shí)根據(jù)需求寫代碼.人工進(jìn)行測試往往不會(huì)面面俱到,還會(huì)因?yàn)樾枨蟮母淖兎爆嵉倪M(jìn)行測試
通過完成一個(gè)測試函數(shù),可以大大簡化測試的步驟,并且在需求該變的時(shí)候只需要改變一下測試的輸入與期望

一、單元測試分類及其概念

1.基本分類

  • 測試函數(shù) 函數(shù)前綴為Test 主要用于測試程序的一些邏輯行為是否正確
  • 基準(zhǔn)函數(shù) 函數(shù)名前綴為Benchmark 主要測試函數(shù)的性能
  • 示例函數(shù) 函數(shù)的前綴名為 Example 為文檔提示示例文檔

2.細(xì)說單元測試分類

①測試函數(shù)

  • 函數(shù)的基本測試
  • 函數(shù)的組測試
  • 函數(shù)的子測試
  • 測試函數(shù)覆蓋率(也就是被測試函數(shù)有多少代碼用于了執(zhí)行)
  • 在進(jìn)行測試的時(shí)候,要保證被測函數(shù)覆蓋率為100%,測試函數(shù)覆蓋率為60%以上,否則寫的代碼大部分用不到,需要優(yōu)化
    • ①測試覆蓋率可以使用go test -cover
    • go test -cover -coverprofile=c.out(將測試的結(jié)果存入文件c.out)
    • 然后使用go tool cover -html=c.out可以打開文件,顯示哪些代碼沒有執(zhí)行

②基準(zhǔn)測試

  • 基準(zhǔn)函數(shù)測試是做什么的:基準(zhǔn)函數(shù)會(huì)自定義一個(gè)時(shí)間段用于執(zhí)行代碼,如果代碼簡潔,被測函數(shù)的執(zhí)行次數(shù)需要成倍增加(直到達(dá)到基準(zhǔn)測試函數(shù)給的預(yù)期,然后統(tǒng)計(jì)一共執(zhí)行了多少輪,,每輪平均用時(shí)多少)
  • 執(zhí)行基準(zhǔn)函數(shù)時(shí),首先要寫基準(zhǔn)函數(shù)
  • 基準(zhǔn)測試函數(shù)的參數(shù)是:*testing.B對(duì)應(yīng)的指針變量
  • 在測試函數(shù)內(nèi)部要進(jìn)行循環(huán),循環(huán)的終止條件是b.N

二、結(jié)合代碼細(xì)說每一種測試

1.基準(zhǔn)測試

(1)基準(zhǔn)測試函數(shù)需要注意的點(diǎn)

進(jìn)行基準(zhǔn)測試時(shí)往往是對(duì)函數(shù)的算法進(jìn)行測驗(yàn),有時(shí)后一個(gè)算法在測試數(shù)據(jù)的基量不同時(shí)測試出的效果會(huì)不同我們需要對(duì)不同數(shù)量級(jí)的樣本進(jìn)行測試的時(shí)候??梢宰约簩懸粋€(gè)盒子作為跳板,測試什么數(shù)量級(jí)的數(shù)據(jù)就只進(jìn)行當(dāng)前數(shù)量級(jí)的測試

 測試的時(shí)候命令為:go test -bench=.(將所有的跳板函數(shù)均執(zhí)行一遍)或者=特定函數(shù)進(jìn)行特定函數(shù)測試-benchtime=time  (在被測試函數(shù)在基準(zhǔn)函數(shù)默認(rèn)的時(shí)間內(nèi)執(zhí)行不完時(shí)可以拿這個(gè)參數(shù)進(jìn)行時(shí)間的擴(kuò)充)在進(jìn)行基準(zhǔn)函數(shù)測試的時(shí)候,可能需要進(jìn)行一些前置工作,如果感覺前置工作浪費(fèi)時(shí)間可以使用 b.ResetTimer()對(duì)計(jì)時(shí)器進(jìn)行重置

(2)基準(zhǔn)測試代碼

測試函數(shù)代碼如下:

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
//文件名 subStr_test.go
package main
 
import (
    "reflect"
    "testing"
)
func BenchmarkSubStr(b *testing.B) {
    for i := 0; i < b.N; i++ {
        res := subStr("qwe:qw:es:wqe", ":")
        if !reflect.DeepEqual(res, []string{"qwe", "qw", "es", "wqe"}) {
            b.Errorf("不匹配")
        }
    }
 
}
 
func benchmarkFib(b *testing.B, n int) {
    for i := 0; i < b.N; i++ {
        Fib(n)
    }
}
 
func BenchmarkFib5(b *testing.B)  { benchmarkFib(b, 5) }
func BenchmarkFib10(b *testing.B) { benchmarkFib(b, 10) }
func BenchmarkFib15(b *testing.B) { benchmarkFib(b, 15) }
func BenchmarkFib20(b *testing.B) { benchmarkFib(b, 20) }

被測函數(shù)代碼如下:

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
//文件名 subStr.go
package main
 
import (
    "fmt"
    "strings"
)
 
func subStr(str, stre string) []string {
    index := strings.Index(str, stre)
    var theSub []string
 
    for index >= 0 {
        // 先將分隔符前面的數(shù)據(jù)進(jìn)行保存
        temp := str[:index]
        // 將字符串往后移
        str = str[index+1:]
        // 重新獲取下標(biāo)
        index = strings.Index(str, stre)
        if temp != "" {
            theSub = append(theSub, temp)
        } else {
            continue
        }
    }
    theSub = append(theSub, str)
    return theSub[:]
}
 
// 菲薄那契數(shù)列
func Fib(n int) int {
    if n < 2 {
        return n
    }
    return Fib(n-1) + Fib(n-2)
}
 
func main() {
    fmt.Println(subStr("q:w:ec:wer:cd:scn:cj:c:is:icc:cin:si", ":"))
    fmt.Printf("%#v\n", subStr("q:w:ec:wer:cd:scn:cj:c:is:icc:cin:si", ":"))
    fmt.Println(123)
}

將以上兩個(gè)文件放在同一目錄下,執(zhí)行測試命令 go test
 

得到測試結(jié)果:

    goos: windows
    goarch: amd64
    cpu: Intel(R) Core(TM) i5-8265U CPU @ 1.60GHz
    BenchmarkSubStr-8   1213681       1012 ns/op      352 B/op  14 allocs/op
    PASS
    ok      _/d_/Go語言學(xué)習(xí)筆記/go語言語法部分/go語言進(jìn)階語法/8.單元測試/基準(zhǔn)測試    2.410s

2.組測試與子測試

  • 對(duì)于一個(gè)函數(shù)的測試,不僅僅是一組測試用例,所以我們需要寫測試組, 對(duì)一個(gè)函數(shù)進(jìn)行測試,測試的時(shí)候一般用集合鍵值對(duì)構(gòu)成,每一個(gè)鍵對(duì)應(yīng)一組測試數(shù)據(jù)
  • 進(jìn)行組測試的時(shí)候可能會(huì)有個(gè)別的組出錯(cuò),所以使用子測試可以對(duì)某個(gè)案例進(jìn)行單獨(dú)測試這就是測試組與子測試的產(chǎn)生背景

測試函數(shù)如下:

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
package main
 
import (
    "reflect"
    "testing"
)
 
type testS struct {
    str  string
    ste  string
    want []string
}
 
//進(jìn)行組測試,批量進(jìn)行測試,如果有哪個(gè)地方不滿足則進(jìn)行異常拋出
// func TestSubStr(t *testing.T) {
//     testMap := map[string]testS{
//         "case_1": {"123:eqwe:123", ":", []string{"123", "eqwe", "123"}},
//         "case_2": {"13:eqwe3:1@23", "3:", []string{"1", "eqwe", "1@23"}},
//         "case_3": {"12@3@:@eq@we@:1@23", "@:", []string{"12@3", "@eq@we", "1@23"}},
//         "case_4": {"123:eq@we:1@2@3", "q@", []string{"123:e", "we:1@2@3"}},
//         "case_5": {"123:eqwe:123", "2", []string{"1", "3:eqwe:1", "3"}},
//         "case_6": {"123:eqwe:123", "eqwe", []string{"123:", ":123"}},
//     }
//     for k, v := range testMap {
//         res := subStr(v.str, v.ste)
//         if !reflect.DeepEqual(res, v.want) {
//             t.Errorf("%v want:%#v got:%#v", k, v.want, res)
//         }
//     }
// }
 
// 進(jìn)行子測試,可以針對(duì)某個(gè)子樣本進(jìn)行測試
func TestSubStr(t *testing.T) {
    testMap := map[string]testS{
        "case_1": {"13:eqwe:123", ":", []string{"123", "eqwe", "123"}},
        "case_2": {"3:eqwe3:1@23", "3:", []string{"1", "eqwe", "1@23"}},
        "case_3": {"2@3@:@eq@we@:1@23", "@:", []string{"12@3", "@eq@we", "1@23"}},
        "case_4": {"123:eq@we:1@2@3", "q@", []string{"123:e", "we:1@2@3"}},
        "case_5": {"23:eqwe:123", "2", []string{"1", "3:eqwe:1", "3"}},
        "case_6": {"123:eqwe:123", "eqwe", []string{"123:", ":123"}},
    }
    for k, v := range testMap {
        t.Run(k, func(t *testing.T) {
            res := subStr(v.str, v.ste)
            if !reflect.DeepEqual(res, v.want) {
                t.Errorf("want:%#v got:%#v", v.want, res)
            }
        })
 
    }
}

待測函數(shù)如下:

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
package main
 
import (
    "fmt"
    "strings"
)
 
func subStr(str, stre string) []string {
    index := strings.Index(str, stre)
    var theSub []string
 
    for index >= 0 {
        // 先將分隔符前面的數(shù)據(jù)進(jìn)行保存
        temp := str[:index]
        // 將字符串往后移
        str = str[index+len(stre):]
        // 重新獲取下標(biāo)
        index = strings.Index(str, stre)
        if temp != "" {
            theSub = append(theSub, temp)
        } else {
            continue
        }
    }
    theSub = append(theSub, str)
    return theSub[:]
}
 
func main() {
    fmt.Println(subStr("q:w:ec:wer:cd:scn:cj:c:is:icc:cin:si", ":c"))
    fmt.Printf("%#v\n", subStr("q:w:ec:wer:cd:scn:cj:c:is:icc:cin:si", ":"))
    fmt.Println(123)
}

(1).組測試結(jié)果分析及命令:

  • 組測試命令仍使用 go test

--- FAIL: TestSubStr (0.00s)
    --- FAIL: TestSubStr/case_1 (0.00s)
       subStr_test.go:46: want:[]string{"123", "eqwe", "123"} got:[]string{"13", "eqwe", "123"}        
    --- FAIL: TestSubStr/case_2 (0.00s)
         subStr_test.go:46: want:[]string{"1", "eqwe", "1@23"} got:[]string{"eqwe", "1@23"}
    --- FAIL: TestSubStr/case_3 (0.00s)
         subStr_test.go:46: want:[]string{"12@3", "@eq@we", "1@23"} got:[]string{"2@3", "@eq@we", "1@23"}
    --- FAIL: TestSubStr/case_5 (0.00s)
         subStr_test.go:46: want:[]string{"1", "3:eqwe:1", "3"} got:[]string{"3:eqwe:1", "3"}
    FAIL
    exit status 1
    FAIL    _/D_/Go語言學(xué)習(xí)筆記/go語言語法部分/go語言進(jìn)階語法/8.單元測試/組測試與子測試     0.155s

(2).子測試結(jié)果分析及命令:

  • 例如單獨(dú)測試case_1,使用的命令為go test -v -run=TestSubStr/case_1(等號(hào)后面跟的是上面組測試失敗案例FAIL后面的目錄)

=== RUN   TestSubStr
=== RUN   TestSubStr/case_1
    subStr_test.go:46: want:[]string{"123", "eqwe", "123"} got:[]string{"13", "eqwe", "123"}
--- FAIL: TestSubStr (0.00s)
    --- FAIL: TestSubStr/case_1 (0.00s)
FAIL
exit status 1
FAIL    _/D_/Go語言學(xué)習(xí)筆記/go語言語法部分/go語言進(jìn)階語法/8.單元測試/組測試與子測試     0.186s

三、pprof調(diào)試工具

1.對(duì)主函數(shù)進(jìn)行傳參

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
(1)os.Args
package main1
 
import (
    "fmt"
    "os"
)
 
func main() {
    // os.Args可以在執(zhí)行函數(shù)的時(shí)候傳遞參數(shù),但是對(duì)于-name=xxx沒有辦法將其解析
    if os.Args != nil {
        for index, temp := range os.Args {
            fmt.Println("第", index+1, "個(gè)參數(shù)是:", temp)
        }
    }
    fmt.Println("hello")
}

(2)flag.Args

  •    相對(duì)于os.Args來說,flag.Args使用起來更加方便
?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
package main
 
import (
    "flag"
    "fmt"
    "time"
)
 
func main() {
    // 第一個(gè)參數(shù)為對(duì)應(yīng)的參數(shù),第二個(gè)參數(shù)為默認(rèn)值,第三個(gè)參數(shù)為提示
    // 返回的是對(duì)應(yīng)類型的指針
    // name := flag.String("name", "Tom", "輸入name")
    // sex := flag.Bool("sex", true, "是不是男性")
    // age := flag.Int("age", 10, "年齡")
    // flag.Parse()
    // fmt.Println(*name, *sex, *age)
    var name string
    var age int
    var sex bool
    flag.StringVar(&name, "name", "Tom", "輸入name")
    flag.BoolVar(&sex, "sex", true, "是不是男性")
    flag.IntVar(&age, "age", 10, "年齡")
    tim := flag.Duration("time", time.Hour, "時(shí)間")
    // 將輸入的數(shù)據(jù)進(jìn)行解析,不使用這句話的話不能獲取到name sex age等屬性對(duì)應(yīng)的值
    flag.Parse()
    fmt.Println(name, sex, age, *tim)
    fmt.Println(flag.Args())  //以切片的方式返回命令行之外的其他參數(shù)
    fmt.Println(flag.NArg())  //返回命令行之外的其他參數(shù)的個(gè)數(shù)
    fmt.Println(flag.NFlag()) //返回使用命令行的參數(shù)的個(gè)數(shù)
}

2.pprof性能調(diào)優(yōu)

pprof調(diào)試工具進(jìn)行調(diào)試,主要看待測模塊在內(nèi)存于時(shí)間上的效益調(diào)試的時(shí)候只會(huì)顯示耗費(fèi)時(shí)間空間較多的代碼段.

生成調(diào)試代碼塊的文件:go run xx.exe -cpu....
使用go語言工具查看代碼塊所存在的問題:go tool pprof cpu.pprof

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
package main
 
import (
    "flag"
    "fmt"
    "os"
    "runtime/pprof"
    "time"
)
// 一段有問題的代碼
func logicCode() {
    var c chan int
    for {
        select {
        case v := <-c:
            fmt.Printf("recv from chan, value:%v\n", v)
        default:
 
        }
    }
}
 
func main() {
    var isCPUPprof bool
    var isMemPprof bool
    //獲取接收到的參數(shù)
    flag.BoolVar(&isCPUPprof, "cpu", false, "turn cpu pprof on")
    flag.BoolVar(&isMemPprof, "mem", false, "turn mem pprof on")
    flag.Parse()
    //判斷進(jìn)行什么測試,然后執(zhí)行分支,并將信息保存到相應(yīng)的文件內(nèi)。
    if isCPUPprof {
        file, err := os.Create("./cpu.pprof")
        if err != nil {
            fmt.Printf("create cpu pprof failed, err:%v\n", err)
            return
        }
        pprof.StartCPUProfile(file)
        defer pprof.StopCPUProfile()
    }
    for i := 0; i < 8; i++ {
        go logicCode()
    }
    time.Sleep(20 * time.Second)
    if isMemPprof {
        file, err := os.Create("./mem.pprof")
        if err != nil {
            fmt.Printf("create mem pprof failed, err:%v\n", err)
            return
        }
        pprof.WriteHeapProfile(file)
        file.Close()
    }
}

總結(jié):

這里對(duì)常用的測試函數(shù),子測試進(jìn)行了詳細(xì)的講解,對(duì)于pprof參數(shù)調(diào)優(yōu)僅僅做了個(gè)介紹,可以根據(jù)自己的興趣在網(wǎng)上找一部分pprof的例子深入學(xué)習(xí),進(jìn)行自動(dòng)化測試非常方便。

到此這篇關(guān)于Go語言單元測試超詳細(xì)解析的文章就介紹到這了,更多相關(guān)Go語言單元測試內(nèi)容請(qǐng)搜索服務(wù)器之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持服務(wù)器之家!

原文鏈接:https://blog.csdn.net/apple_51931783/article/details/122563019

延伸 · 閱讀

精彩推薦
  • GolangGolang通脈之?dāng)?shù)據(jù)類型詳情

    Golang通脈之?dāng)?shù)據(jù)類型詳情

    這篇文章主要介紹了Golang通脈之?dāng)?shù)據(jù)類型,在編程語言中標(biāo)識(shí)符就是定義的具有某種意義的詞,比如變量名、常量名、函數(shù)名等等,Go語言中標(biāo)識(shí)符允許由...

    4272021-11-24
  • Golanggo日志系統(tǒng)logrus顯示文件和行號(hào)的操作

    go日志系統(tǒng)logrus顯示文件和行號(hào)的操作

    這篇文章主要介紹了go日志系統(tǒng)logrus顯示文件和行號(hào)的操作,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過來看看吧...

    SmallQinYan12302021-02-02
  • GolangGolang中Bit數(shù)組的實(shí)現(xiàn)方式

    Golang中Bit數(shù)組的實(shí)現(xiàn)方式

    這篇文章主要介紹了Golang中Bit數(shù)組的實(shí)現(xiàn)方式,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過來看看吧...

    天易獨(dú)尊11682021-06-09
  • Golanggolang的httpserver優(yōu)雅重啟方法詳解

    golang的httpserver優(yōu)雅重啟方法詳解

    這篇文章主要給大家介紹了關(guān)于golang的httpserver優(yōu)雅重啟的相關(guān)資料,文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,...

    helight2992020-05-14
  • Golanggo語言制作端口掃描器

    go語言制作端口掃描器

    本文給大家分享的是使用go語言編寫的TCP端口掃描器,可以選擇IP范圍,掃描的端口,以及多線程,有需要的小伙伴可以參考下。 ...

    腳本之家3642020-04-25
  • Golanggolang如何使用struct的tag屬性的詳細(xì)介紹

    golang如何使用struct的tag屬性的詳細(xì)介紹

    這篇文章主要介紹了golang如何使用struct的tag屬性的詳細(xì)介紹,從例子說起,小編覺得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過來看...

    Go語言中文網(wǎng)11352020-05-21
  • Golanggolang json.Marshal 特殊html字符被轉(zhuǎn)義的解決方法

    golang json.Marshal 特殊html字符被轉(zhuǎn)義的解決方法

    今天小編就為大家分享一篇golang json.Marshal 特殊html字符被轉(zhuǎn)義的解決方法,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過來看看吧 ...

    李浩的life12792020-05-27
  • Golanggolang 通過ssh代理連接mysql的操作

    golang 通過ssh代理連接mysql的操作

    這篇文章主要介紹了golang 通過ssh代理連接mysql的操作,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過來看看吧...

    a165861639710342021-03-08
主站蜘蛛池模板: 亚洲系列国产精品制服丝袜第 | 成年人在线观看视频 | 91精品国产高清久久久久久 | 奇米影视久久777中文字幕 | 午夜精品久久久内射近拍高清 | 亚洲天堂在线视频播放 | 欧美多gayxxxx| 狠狠的撞进去嗯啊h女强男视频 | 禁忌h1v1怀孕 | 久见久热 这里只有精品 | 91私密保健女子养生spa | 精品国产福利片在线观看 | 国产v视频| 欧美性高清另类videosex死尸 | 久久大胆视频 | 二次元美女互摸隐私互扒 | 嫩草视频在线观看免费 | 996免费视频国产在线播放 | 饭冈加奈子黑人解禁在线播放 | 国产成人精视频在线观看免费 | 成人久久伊人精品伊人 | 好大好猛好爽好深视频免费 | 国产午夜成人无码免费看 | 逼毛片 | 色五婷婷| 国产成人精品免费视频大全五级 | 高清国语自产拍免费视频国产 | 亚洲va久久久噜噜噜久久狠狠 | 韩国三级视频网站 | 果冻传媒在线视频观看免费 | 美女扒开胸罩露出奶 | 精油按摩日本 | 国产精品自在线拍 | 欧美老人与小伙子性生交 | 校草让我脱了内裤给全班看 | 欧美一级裸片又黄又裸 | 亚洲日韩精品欧美一区二区 | 亚洲国产成人在线视频 | 67194最新网址 | 风间由美在线播放 | 国产精品一区二区三区免费 |