亚洲乱码中文字幕综合,中国熟女仑乱hd,亚洲精品乱拍国产一区二区三区,一本大道卡一卡二卡三乱码全集资源,又粗又黄又硬又爽的免费视频

詳解Golang中單元測試的使用

 更新時(shí)間:2023年07月03日 15:32:33   作者:想去海南摘椰子  
單元測試是檢測你寫的一個(gè)函數(shù)是否具備安全性的一次檢測,這篇文章主要為大家詳細(xì)介紹了Golang中單元測試的具體使用,希望對大家有所幫助

介紹

單元測試有什么用?

單元測試是檢測你寫的一個(gè)函數(shù)是否具備安全性的一次檢測。比如,當(dāng)你寫了一個(gè)函數(shù),傳入你期望的值,該函數(shù)是能正常執(zhí)行的。但是當(dāng)你傳入一個(gè)非合理的值后,你寫的函數(shù)直接宕機(jī),而不是返回err。那就說明你寫的函數(shù)存在邏輯漏洞。一個(gè)通過單元測試的函數(shù),無論傳入什么類型的值,返回的要么是結(jié)果,要么是error,絕對不會(huì)停止運(yùn)行。

go test工具

在golang語言中存在測試依賴go test命令,go test命令會(huì)對包路徑下的所有_test.go為后綴的源代碼文件進(jìn)行測試,不會(huì)被go build編譯到可執(zhí)行文件中。

在*_test.go文件中,包含三種類型的函數(shù):單元測試函數(shù)、基準(zhǔn)測試函數(shù)、示例函數(shù)

類型文件名作用
測試函數(shù)TestXxxx測試程序的一些邏輯行為是否正確
基準(zhǔn)函數(shù)BenchchmarkXxxx測試函數(shù)的性能
示例函數(shù)ExampleXxxx為文檔提供示例文檔

go test 命令會(huì)對遍歷*_test.go文件中的符合上述規(guī)范的函數(shù),生成一個(gè)臨時(shí)的main包用于調(diào)用相應(yīng)的測試函數(shù),重構(gòu)、運(yùn)行、報(bào)告、測試,最后清除測試中的生成的臨時(shí)文件。

測試函數(shù)

每個(gè)測試函數(shù)都必須導(dǎo)入testing包。

// 用于對str字符串的子串截取
func subString(str string, start, end int) string {
   //將str轉(zhuǎn)換為[]rune,支持Unicode編碼
   rs := []rune(str)
   length := len(rs)
   if start < 0 || start > length {
      panic("start is wrong")
   }
   if end < start || end > length {
      panic("end is wrong")
   }
   return string(rs[start:end])
}

上述函數(shù)傳入str、start、end參數(shù),分別表示原始字符串,起始下標(biāo),結(jié)束下標(biāo)。目的是為了獲取str的子串。

這里有個(gè)知識(shí)點(diǎn),是關(guān)于go語言的編碼的:

在golang中,其采納的編碼方式是UTF8的支持中文編碼方式,而非Unicode。在UTF8中,一個(gè)漢字是用三個(gè)字節(jié)來表示的。舉個(gè)列子,str := "hang行",其len(str)==7,如果你從start ==5 ,end == 6切割就會(huì)出現(xiàn)亂碼,相當(dāng)于你截取了漢字的三分之一。當(dāng)str轉(zhuǎn)換為[]rune類型后,len([]rune(str)) == 5,這個(gè)時(shí)候漢字就是一個(gè)整體,就能完整的截取"行"這個(gè)漢字。

func TestCode(t *testing.T) {
   // []rune與Unicode
   str := "i am your 爸爸"
   fmt.Printf("str的原始長度:%d\n", len(str))
   fmt.Printf("str的[]rune類型長度:%d\n", len([]rune(str)))
}

打印信息:

API server listening at: 127.0.0.1:64049
WARNING: undefined behavior - version of Delve is too old for Go version 1.19.0 (maximum supported version 1.18)
=== RUN   TestCode
str的原始長度:16
str的[]rune類型長度:12
--- PASS: TestCode (0.00s)
PASS

對[]rune的測試完畢,回到測試函數(shù)中:

func TestSubString(t *testing.T) {
   got := subString("hang", 0, 1)
   want := "h"
   if !reflect.DeepEqual(got, want) {
      t.Error("subString test is failed")
   }
}

打印信息:

API server listening at: 127.0.0.1:57824
WARNING: undefined behavior - version of Delve is too old for Go version 1.19.0 (maximum supported version 1.18)
=== RUN   TestSubString
--- PASS: TestSubString (0.00s)
PASS

在該測試函數(shù)中,got為函數(shù)返回信息;want為期望值。經(jīng)過reflect.DeepEqual(got,want)函數(shù)進(jìn)行匹配比較。

reflect.DeepEqual(got,want)函數(shù):用于比較兩個(gè)值的深度相等性。它可以用來在測試中判斷期望的值和實(shí)際得到的值是否相等。reflect.DeepEqual對比的是值的具體內(nèi)容,而不僅僅是內(nèi)存地址或類型。用于確保值的類型和結(jié)構(gòu)是一致的。

testing.T類型的方法作用
Error輸出一個(gè)錯(cuò)誤消息,并標(biāo)記該測試為失敗。
Errorf格式化輸出一個(gè)錯(cuò)誤消息,并標(biāo)記該測試為失敗。
Fail標(biāo)記該測試為失敗。
FailNow標(biāo)記該測試為失敗,并立即停止當(dāng)前測試函數(shù)的執(zhí)行。
Fatal輸出一個(gè)致命錯(cuò)誤消息,并終止測試執(zhí)行
Fatalf格式化輸出一個(gè)致命錯(cuò)誤消息,并終止測試執(zhí)行。
Name返回當(dāng)前測試的名稱。
Run運(yùn)行一個(gè)子測試函數(shù),并指定其名稱。

目前常用的testing.T類型的打印信息函數(shù)是上訴這幾個(gè)。如果對其他函數(shù)感興趣,可以查閱資料。

測試函數(shù)是能夠執(zhí)行了,但是只能針對一個(gè)測試用例。如果有多個(gè)測試用例呢?這就引入了子測試函數(shù)的使用:

func TestSubString(t *testing.T) {
   type test struct {
      str   string
      start int
      end   int
      want  interface{}
   }
   es := map[string]test{
      "basic":   {str: "basic", start: 0, end: 4, want: "basi"}, //basi
      "basicCN": {str: "你好world", start: 0, end: 1, want: "你"},  //"你"
   }
   for name, testValue := range es {
      t.Run(name, func(t *testing.T) {
         got := subString(testValue.str, testValue.start, testValue.end)
         if !reflect.DeepEqual(got, testValue.want) {
            t.Errorf("the name== %s got %v, want %v", t.Name(), got, testValue.want)
         }
      })
   }
}

打印信息:

API server listening at: 127.0.0.1:64621
WARNING: undefined behavior - version of Delve is too old for Go version 1.19.0 (maximum supported version 1.18)
=== RUN   TestSubString
=== RUN   TestSubString/basic
=== RUN   TestSubString/basicCN
--- PASS: TestSubString (0.00s)
    --- PASS: TestSubString/basic (0.00s)
    --- PASS: TestSubString/basicCN (0.00s)
PASS

說明:在我寫的函數(shù)SubString中,有 panic("start is wrong") 語句,這個(gè)結(jié)果在測試單元中是無法want的,即這種結(jié)果是無法測試。但是這個(gè)函數(shù)對我的項(xiàng)目是很重要的,未達(dá)到start和end就必須要panic。無需去深究,你可以將panic換成error.new("XXXX")。測試函數(shù)的部分知識(shí)就講解完畢了,文章后續(xù)有案例實(shí)戰(zhàn)。

基準(zhǔn)測試

基準(zhǔn)測試是在一定的工作負(fù)載下檢測程序性能的一種方法。函數(shù)一定是先通過測試函數(shù)之后,才開始對該函數(shù)性能的檢測。很多程序員在開發(fā)了一套函數(shù)時(shí),就會(huì)有一種心態(tài):能跑就行。其實(shí)這樣想也沒問題,工作嘛。基準(zhǔn)測試也是檢測自身編寫代碼的性能。很簡單一個(gè)道理,同樣是寫一樣功能的代碼,為什么就用其他的,而不用你的,因?yàn)槠渌脑谛阅軠y試上超越了你。

基準(zhǔn)測試是以Benchmark為開頭的,需要以*testing.B類型的參數(shù)。

func BenchmarkSubstring(b *testing.B) {
   for i := 0; i < b.N; i++ {
      subString("Benchmark", 0, 5)
   }
}

打印信息:

goos: windows
goarch: amd64
pkg: sspaas.io/gpu-manage/utils/data
cpu: Intel(R) Core(TM) i5-9300H CPU @ 2.40GHz
BenchmarkSubstring
BenchmarkSubstring-8    14777178                81.77 ns/op
PASS

相較于單元測試,基準(zhǔn)測試使用得不是很多。

BenchmarkSubstring-8    14777178                81.77 ns/op

表示Substring函數(shù)在迭代14777178次下,平均每次消耗81.77 納秒。b.N默認(rèn)是很大的一個(gè)數(shù),你也可以自定義迭代次數(shù)。一般是不要去動(dòng)b.N

基準(zhǔn)測試還有很多知識(shí)點(diǎn),包括內(nèi)存消耗的檢測、測試代碼的覆蓋率等。一般來說,作為開發(fā)人員僅僅測試一下迭代時(shí)間即可。若想了解更多,自行查閱資料。

示例函數(shù)

在單元測試中,示例函數(shù)(Example Functions)是一種特殊的函數(shù),用于提供對被測試函數(shù)的示例使用方法、參數(shù)和期望輸出的演示。

func ExampleFunction() {
	result := YourFunction(input)
	fmt.Println(result)
	// Output: expected_output
}

閑的沒事的人,才去寫示例函數(shù)。有這時(shí)間,還不如去泡杯茶····

實(shí)戰(zhàn)

對aes加密解密的aes.go文件進(jìn)行單元測試

import (
   "bytes"
   "crypto/aes"
   "crypto/cipher"
   "encoding/base64"
)
// AesEncrypt AES加密
func AesEncrypt(orig string, key string) string {
   // 轉(zhuǎn)換成字節(jié)數(shù)組
   origData := []byte(orig)
   k := []byte(key)
   // 分組秘鑰
   block, _ := aes.NewCipher(k)
   // 獲取秘鑰塊的長度
   blockSize := block.BlockSize()
   // 補(bǔ)全碼
   origData = PKCS7Padding(origData, blockSize)
   // 加密模式
   blockMode := cipher.NewCBCEncrypter(block, k[:blockSize])
   // 創(chuàng)建數(shù)組
   cryted := make([]byte, len(origData))
   // 加密
   blockMode.CryptBlocks(cryted, origData)
   return base64.StdEncoding.EncodeToString(cryted)
}
// AesDecrypt AES解密
func AesDecrypt(cryted string, key string) string {
   // 轉(zhuǎn)成字節(jié)數(shù)組
   crytedByte, _ := base64.StdEncoding.DecodeString(cryted)
   k := []byte(key)
   // 分組秘鑰
   block, _ := aes.NewCipher(k)
   // 獲取秘鑰塊的長度
   blockSize := block.BlockSize()
   // 加密模式
   blockMode := cipher.NewCBCDecrypter(block, k[:blockSize])
   // 創(chuàng)建數(shù)組
   orig := make([]byte, len(crytedByte))
   // 解密
   blockMode.CryptBlocks(orig, crytedByte)
   // 去補(bǔ)全碼
   orig = PKCS7UnPadding(orig)
   return string(orig)
}
// PKCS7Padding 補(bǔ)碼
func PKCS7Padding(ciphertext []byte, blocksize int) []byte {
   padding := blocksize - len(ciphertext)%blocksize
   padtext := bytes.Repeat([]byte{byte(padding)}, padding)
   return append(ciphertext, padtext...)
}
// PKCS7UnPadding 去碼
func PKCS7UnPadding(origData []byte) []byte {
   length := len(origData)
   unpadding := int(origData[length-1])
   return origData[:(length - unpadding)]
}
import (
   "reflect"
   "testing"
)
/*----------------------單元測試----------------------------------------*/
const key = "q+#@,6%ej-QP^mbc"
func TestAesEncrypt(t *testing.T) {
   type test struct {
      org  string
      key  string
      want string
   }
   es := map[string]test{
      "passed": {org: "basic", key: key, want: "2+1s9cqk1NzIUnq+G3VgEg=="}, //passed
   }
   for _, value := range es {
      got := AesEncrypt(value.org, value.key)
      if !reflect.DeepEqual(got, value.want) {
         t.Errorf("the name== %s got %v, want %v", t.Name(), got, value.want)
      }
   }
}
/*
API server listening at: 127.0.0.1:54671
WARNING: undefined behavior - version of Delve is too old for Go version 1.19.0 (maximum supported version 1.18)
=== RUN   TestAesEncrypt
--- PASS: TestAesEncrypt (0.00s)
PASS
*/
func BenchmarkAesEncrypt(b *testing.B) {
   for i := 0; i < b.N; i++ {
      AesEncrypt("test", key)
   }
}
/*pkg: sspaas.io/gpu-manage/utils/aes
cpu: Intel(R) Core(TM) i5-9300H CPU @ 2.40GHz
BenchmarkAesEncrypt
BenchmarkAesEncrypt-8            1000000              1031 ns/op
PASS
*/
func TestAesDecrypt(t *testing.T) {
   type test struct {
      org  string
      key  string
      want string
   }
   es := map[string]test{
      "passed": {org: "2+1s9cqk1NzIUnq+G3VgEg==", key: key, want: "basic"}, //passed
   }
   for _, value := range es {
      got := AesDecrypt(value.org, value.key)
      if !reflect.DeepEqual(got, value.want) {
         t.Errorf("the name== %s got %v, want %v", t.Name(), got, value.want)
      }
   }
}
/*API server listening at: 127.0.0.1:54693
WARNING: undefined behavior - version of Delve is too old for Go version 1.19.0 (maximum supported version 1.18)
=== RUN   TestAesDecrypt
--- PASS: TestAesDecrypt (0.00s)
PASS
*/
func BenchmarkAesDecrypt(b *testing.B) {
   for i := 0; i < b.N; i++ {
      AesDecrypt("2+1s9cqk1NzIUnq+G3VgEg==", key)
   }
}
/*pkg: sspaas.io/gpu-manage/utils/aes
cpu: Intel(R) Core(TM) i5-9300H CPU @ 2.40GHz
BenchmarkAesDecrypt
BenchmarkAesDecrypt-8            1334907               883.0 ns/op
PASS
*/

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

相關(guān)文章

  • Go定時(shí)器的三種實(shí)現(xiàn)方式示例詳解

    Go定時(shí)器的三種實(shí)現(xiàn)方式示例詳解

    這篇文章主要為大家介紹了Go定時(shí)器的三種實(shí)現(xiàn)方式示例詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪
    2022-12-12
  • go?micro微服務(wù)proto開發(fā)安裝及使用規(guī)則

    go?micro微服務(wù)proto開發(fā)安裝及使用規(guī)則

    這篇文章主要為大家介紹了go?micro微服務(wù)proto開發(fā)中安裝Protobuf及基本規(guī)范字段的規(guī)則詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪
    2023-01-01
  • golang逗號(hào)ok模式整合demo

    golang逗號(hào)ok模式整合demo

    這篇文章主要為大家介紹了golang逗號(hào)ok模式整合demo,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪
    2023-11-11
  • Go語言中常用語法編寫與優(yōu)化技巧小結(jié)

    Go語言中常用語法編寫與優(yōu)化技巧小結(jié)

    為了充分利用?Go?的潛力,我們需要了解如何優(yōu)化?Go?程序,本文將介紹一些常見的?Go?語言優(yōu)化技巧,并通過實(shí)際例子進(jìn)行說明,希望對大家有所幫助
    2024-02-02
  • 一文帶你了解Golang中的泛型

    一文帶你了解Golang中的泛型

    泛型是一種可以編寫?yīng)毩⒂谑褂玫奶囟愋偷拇a的方法,可以通過編寫函數(shù)或類型來使用一組類型中的任何一個(gè),下面就來和大家聊聊Golang中泛型的使用吧
    2023-07-07
  • golang json.Marshal 特殊html字符被轉(zhuǎn)義的解決方法

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

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

    go各種import的使用方法講解

    今天小編就為大家分享一篇關(guān)于go各種import的使用方法講解,小編覺得內(nèi)容挺不錯(cuò)的,現(xiàn)在分享給大家,具有很好的參考價(jià)值,需要的朋友一起跟隨小編來看看吧
    2019-04-04
  • 最新版Golang?pprof使用詳解(引入、抓取、分析,圖文結(jié)合)

    最新版Golang?pprof使用詳解(引入、抓取、分析,圖文結(jié)合)

    這篇文章主要介紹了最新版Golang?pprof使用詳解包括引入、抓取、分析,圖文結(jié)合,本文給大家介紹的非常詳細(xì),對大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友參考下吧
    2024-08-08
  • Prometheus Go client library使用方式詳解

    Prometheus Go client library使用方式詳解

    這篇文章主要為大家介紹了Prometheus Go client library使用方式詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪
    2023-11-11
  • go語言實(shí)現(xiàn)同步操作項(xiàng)目示例

    go語言實(shí)現(xiàn)同步操作項(xiàng)目示例

    本文主要介紹了go語言實(shí)現(xiàn)同步操作項(xiàng)目示例,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧
    2023-05-05

最新評(píng)論