最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

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

 更新時間:2022年02月25日 16:29:06   作者:酷爾。  
本文介紹了了Go語言單元測試超詳細(xì)解析,測試函數(shù)分為函數(shù)的基本測試、函數(shù)的組測試、函數(shù)的子測試,進(jìn)行基準(zhǔn)測試時往往是對函數(shù)的算法進(jìn)行測驗(yàn),有時后一個算法在測試數(shù)據(jù)的基量不同時測試出的效果會不同我們需要對不同數(shù)量級的樣本進(jìn)行測試,下文需要的朋友可以參考下

前言:

平時根據(jù)需求寫代碼.人工進(jìn)行測試往往不會面面俱到,還會因?yàn)樾枨蟮母淖兎爆嵉倪M(jìn)行測試
通過完成一個測試函數(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ù)覆蓋率為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ù)會自定義一個時間段用于執(zhí)行代碼,如果代碼簡潔,被測函數(shù)的執(zhí)行次數(shù)需要成倍增加(直到達(dá)到基準(zhǔn)測試函數(shù)給的預(yù)期,然后統(tǒng)計(jì)一共執(zhí)行了多少輪,,每輪平均用時多少)
  • 執(zhí)行基準(zhǔn)函數(shù)時,首先要寫基準(zhǔn)函數(shù)
  • 基準(zhǔn)測試函數(shù)的參數(shù)是:*testing.B對應(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ù)的算法進(jìn)行測驗(yàn),有時后一個算法在測試數(shù)據(jù)的基量不同時測試出的效果會不同我們需要對不同數(shù)量級的樣本進(jìn)行測試的時候??梢宰约簩懸粋€盒子作為跳板,測試什么數(shù)量級的數(shù)據(jù)就只進(jìn)行當(dāng)前數(shù)量級的測試

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

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

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

//文件名 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ù)代碼如下:

//文件名 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)
}

將以上兩個文件放在同一目錄下,執(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.組測試與子測試

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

測試函數(shù)如下:

package main

import (
?? ?"reflect"
?? ?"testing"
)

type testS struct {
?? ?str ?string
?? ?ste ?string
?? ?want []string
}

//進(jìn)行組測試,批量進(jìn)行測試,如果有哪個地方不滿足則進(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)行子測試,可以針對某個子樣本進(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ù)如下:

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(等號后面跟的是上面組測試失敗案例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.對主函數(shù)進(jìn)行傳參

(1)os.Args
package main1

import (
?? ?"fmt"
?? ?"os"
)

func main() {
?? ?// os.Args可以在執(zhí)行函數(shù)的時候傳遞參數(shù),但是對于-name=xxx沒有辦法將其解析
?? ?if os.Args != nil {
?? ??? ?for index, temp := range os.Args {
?? ??? ??? ?fmt.Println("第", index+1, "個參數(shù)是:", temp)
?? ??? ?}
?? ?}
?? ?fmt.Println("hello")
}

(2)flag.Args

  •    相對于os.Args來說,flag.Args使用起來更加方便
package main

import (
?? ?"flag"
?? ?"fmt"
?? ?"time"
)

func main() {
?? ?// 第一個參數(shù)為對應(yīng)的參數(shù),第二個參數(shù)為默認(rèn)值,第三個參數(shù)為提示
?? ?// 返回的是對應(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ù)據(jù)進(jìn)行解析,不使用這句話的話不能獲取到name sex age等屬性對應(yīng)的值
?? ?flag.Parse()
?? ?fmt.Println(name, sex, age, *tim)
?? ?fmt.Println(flag.Args()) ?//以切片的方式返回命令行之外的其他參數(shù)
?? ?fmt.Println(flag.NArg()) ?//返回命令行之外的其他參數(shù)的個數(shù)
?? ?fmt.Println(flag.NFlag()) //返回使用命令行的參數(shù)的個數(shù)
}

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

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

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

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é):

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

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

相關(guān)文章

  • goland服務(wù)熱重啟的配置文件

    goland服務(wù)熱重啟的配置文件

    這篇文章主要介紹了goland服務(wù)熱重啟的配置文件,本文給大家介紹的非常詳細(xì),對大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下
    2020-12-12
  • 淺談Golang 嵌套 interface 的賦值問題

    淺談Golang 嵌套 interface 的賦值問題

    這篇文章主要介紹了淺談Golang 嵌套 interface 的賦值問題,具有很好的參考價(jià)值,希望對大家有所幫助。一起跟隨小編過來看看吧
    2021-04-04
  • go 生成器模式的具體使用

    go 生成器模式的具體使用

    生成器是一種創(chuàng)建型設(shè)計(jì)模式,使你能夠分步驟創(chuàng)建復(fù)雜對象,本文主要介紹了go生成器模式的具體使用,具有一定的參考價(jià)值,感興趣的可以了解一下
    2024-01-01
  • golang gc的內(nèi)部優(yōu)化詳細(xì)介紹

    golang gc的內(nèi)部優(yōu)化詳細(xì)介紹

    Go編譯器在垃圾回收(GC)的掃描標(biāo)記階段,對存儲無指針鍵值對的map進(jìn)行了優(yōu)化,即在GC掃描時不深入掃描map內(nèi)部數(shù)據(jù),只檢查map本身是否需要回收,這一優(yōu)化顯著提升了GC掃描的速度,從而減少了GC對程序性能的影響
    2024-10-10
  • 使用golang-unsafe包的注意事項(xiàng)及說明

    使用golang-unsafe包的注意事項(xiàng)及說明

    這篇文章主要介紹了使用golang-unsafe包的注意事項(xiàng)及說明,具有很好的參考價(jià)值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教
    2023-02-02
  • Go反射中type和kind區(qū)別比較詳析

    Go反射中type和kind區(qū)別比較詳析

    這篇文章主要給大家介紹了關(guān)于Go反射中type和kind區(qū)別比較的相關(guān)資料,Type是接口類型,Value是Struct類型,Type是類型描述,而Value是具體的值,需要的朋友可以參考下
    2023-10-10
  • Go?語言結(jié)構(gòu)體鏈表的基本操作

    Go?語言結(jié)構(gòu)體鏈表的基本操作

    鏈表是一種物理存儲單元上非連續(xù)、非順序的存儲結(jié)構(gòu),數(shù)據(jù)元素的邏輯順序是通過鏈表中的指針鏈接次序?qū)崿F(xiàn)的,這篇文章主要介紹了Go?語言結(jié)構(gòu)體鏈表,需要的朋友可以參考下
    2022-04-04
  • Golang發(fā)送Get和Post請求的實(shí)現(xiàn)

    Golang發(fā)送Get和Post請求的實(shí)現(xiàn)

    做第三方接口有時需要用Get或者Post請求訪問,本文主要介紹了Golang發(fā)送Get和Post請求的實(shí)現(xiàn),文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧
    2024-05-05
  • Go語言Zap庫Logger的定制化和封裝使用詳解

    Go語言Zap庫Logger的定制化和封裝使用詳解

    這篇文章主要介紹了Go語言Zap庫Logger的定制化和封裝使用詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪
    2022-06-06
  • 用go gin server來做文件上傳服務(wù)

    用go gin server來做文件上傳服務(wù)

    今天小編就為大家分享一篇關(guān)于用go gin server來做文件上傳服務(wù),小編覺得內(nèi)容挺不錯的,現(xiàn)在分享給大家,具有很好的參考價(jià)值,需要的朋友一起跟隨小編來看看吧
    2019-04-04

最新評論

岫岩| 赤城县| 东台市| 白城市| 兴化市| 高青县| 西充县| 福贡县| 朝阳县| 呼玛县| 鸡东县| 中牟县| 保康县| 儋州市| 全椒县| 平乐县| 延安市| 达孜县| 桑植县| 勃利县| 巴青县| 五河县| 葵青区| 成武县| 微博| 青冈县| 晋中市| 河池市| 伊金霍洛旗| 大关县| 论坛| 永胜县| 枝江市| 乡城县| 嵊州市| 鄂温| 嘉定区| 叶城县| 鄄城县| 罗平县| 英山县|