欧美阿v视频在线大全_亚洲欧美中文日韩V在线观看_www性欧美日韩欧美91_亚洲欧美日韩久久精品

主頁 > 知識庫 > 一步步教你編寫可測試的Go語言代碼

一步步教你編寫可測試的Go語言代碼

熱門標簽:最短的地圖標注 浙江人工智能外呼管理系統 電銷機器人可以補救房產中介嗎 百度地圖標注搜索關鍵詞 成都呼叫中心外呼系統平臺 谷歌便利店地圖標注 電梯外呼訪客系統 騰訊外呼系統價格 ?兓?

第一個測試 “Hello Test!”

首先,在我們$GOPATH/src目錄下創建hello目錄,作為本文涉及到的所有示例代碼的根目錄。

然后,新建名為hello.go的文件,定義一個函數hello() ,功能是返回一個由若干單詞拼接成句子:

package hello

func hello() string {
 words := []string{"hello", "func", "in", "package", "hello"}
 wl := len(words)

 sentence := ""
 for key, word := range words {
  sentence += word
  if key  wl-1 {
   sentence += " "
  } else {
   sentence += "."
  }
 }
 return sentence
}

接著,新建名為hello_test.go的文件,填入如下內容:

package hello

import (
 "fmt"
 "testing"
)

func TestHello(t *testing.T) {
 got := hello()
 expect := "hello func in package hello."

 if got != expect {
  t.Errorf("got [%s] expected [%s]", got, expect)
 }
}

func BenchmarkHello(b *testing.B) {
 for i := 0; i  b.N; i++ {
  hello()
 }
}

func ExampleHello() {
 hl := hello()
 fmt.Println(hl)
 // Output: hello func in package hello.
}

最后,打開終端,進入hello目錄,輸入go test命令并回車,可以看到如下輸出:

PASS
ok  hello 0.007s

編寫測試代碼

Golang的測試代碼位于某個包的源代碼中名稱以_test.go結尾的源文件里,測試代碼包含測試函數、測試輔助代碼和示例函數;測試函數有以Test開頭的功能測試函數和以Benchmark開頭的性能測試函數兩種,測試輔助代碼是為測試函數服務的公共函數、初始化函數、測試數據等,示例函數則是以Example開頭的說明被測試函數用法的函數。

大部分情況下,測試代碼是作為某個包的一部分,意味著它可以訪問包中不可導出的元素。但在有需要的時候(如避免循環依賴)也可以修改測試文件的包名,如package hello的測試文件,包名可以設為package hello_test。

功能測試函數

功能測試函數需要接收*testing.T類型的單一參數t,testing.T 類型用來管理測試狀態和支持格式化的測試日志。測試日志在測試執行過程中積累起來,完成后輸出到標準錯誤輸出。

下面是從Go標準庫摘抄的 testing.T類型的常用方法的用法:

測試函數中的某條測試用例執行結果與預期不符時,調用t.Error()t.Errorf()方法記錄日志并標記測試失敗

# /usr/local/go/src/bytes/compare_test.go
func TestCompareIdenticalSlice(t *testing.T) {
 var b = []byte("Hello Gophers!")
 if Compare(b, b) != 0 {
  t.Error("b != b")
 }
 if Compare(b, b[:1]) != 1 {
  t.Error("b > b[:1] failed")
 }
}

使用t.Fatal()t.Fatalf()方法,在某條測試用例失敗后就跳出該測試函數

# /usr/local/go/src/bytes/reader_test.go
func TestReadAfterBigSeek(t *testing.T) {
 r := NewReader([]byte("0123456789"))
 if _, err := r.Seek(131+5, os.SEEK_SET); err != nil {
  t.Fatal(err)
 }
 if n, err := r.Read(make([]byte, 10)); n != 0 || err != io.EOF {
  t.Errorf("Read = %d, %v; want 0, EOF", n, err)
 }
}

使用t.Skip()t.Skipf()方法,跳過某條測試用例的執行

# /usr/local/go/src/archive/zip/zip_test.go
func TestZip64(t *testing.T) {
 if testing.Short() {
  t.Skip("slow test; skipping")
 }
 const size = 1  32 // before the "END\n" part
 buf := testZip64(t, size)
 testZip64DirectoryRecordLength(buf, t)
}

執行測試用例的過程中通過t.Log()t.Logf()記錄日志

# /usr/local/go/src/regexp/exec_test.go
func TestFowler(t *testing.T) {
 files, err := filepath.Glob("testdata/*.dat")
 if err != nil {
  t.Fatal(err)
 }
 for _, file := range files {
  t.Log(file)
  testFowler(t, file)
 }
}

使用t.Parallel()標記需要并發執行的測試函數

# /usr/local/go/src/runtime/stack_test.go
func TestStackGrowth(t *testing.T) {
 t.Parallel()
 var wg sync.WaitGroup

 // in a normal goroutine
 wg.Add(1)
 go func() {
  defer wg.Done()
  growStack()
 }()
 wg.Wait()

 // ...
}

性能測試函數

性能測試函數需要接收*testing.B類型的單一參數b,性能測試函數中需要循環b.N次調用被測函數。testing.B 類型用來管理測試時間和迭代運行次數,也支持和testing.T相同的方式管理測試狀態和格式化的測試日志,不一樣的是testing.B的日志總是會輸出。

下面是從Go標準庫摘抄的 testing.B類型的常用方法的用法:

在函數中調用t.ReportAllocs() ,啟用內存使用分析

# /usr/local/go/src/bufio/bufio_test.go
func BenchmarkWriterFlush(b *testing.B) {
 b.ReportAllocs()
 bw := NewWriter(ioutil.Discard)
 str := strings.Repeat("x", 50)
 for i := 0; i  b.N; i++ {
  bw.WriteString(str)
  bw.Flush()
 }
}

通過 b.StopTimer() b.ResetTimer() b.StartTimer()來停止、重置、啟動 時間經過和內存分配計數

# /usr/local/go/src/fmt/scan_test.go
func BenchmarkScanInts(b *testing.B) {
 b.ResetTimer()
 ints := makeInts(intCount)
 var r RecursiveInt
 for i := b.N - 1; i >= 0; i-- {
  buf := bytes.NewBuffer(ints)
  b.StartTimer()
  scanInts(r, buf)
  b.StopTimer()
 }
}

調用b.SetBytes()記錄在一個操作中處理的字節數

# /usr/local/go/src/testing/benchmark.go
func BenchmarkFields(b *testing.B) {
 b.SetBytes(int64(len(fieldsInput)))
 for i := 0; i  b.N; i++ {
  Fields(fieldsInput)
 }
}

通過b.RunParallel()方法和 *testing.PB類型的Next()方法來并發執行被測對象

# /usr/local/go/src/sync/atomic/value_test.go
func BenchmarkValueRead(b *testing.B) {
 var v Value
 v.Store(new(int))
 b.RunParallel(func(pb *testing.PB) {
  for pb.Next() {
   x := v.Load().(*int)
   if *x != 0 {
    b.Fatalf("wrong value: got %v, want 0", *x)
   }
  }
 })
}

測試輔助代碼

測試輔助代碼是編寫測試代碼過程中因代碼重用和代碼質量考慮而產生的。主要包括如下方面:

引入依賴的外部包,如每個測試文件都需要的 testing 包等:

# /usr/local/go/src/log/log_test.go:
import (
 "bytes"
 "fmt"
 "os"
 "regexp"
 "strings"
 "testing"
 "time"
)

定義多次用到的常量和變量,測試用例數據等:

# /usr/local/go/src/log/log_test.go:
const (
 Rdate   = `[0-9][0-9][0-9][0-9]/[0-9][0-9]/[0-9][0-9]`
 Rtime   = `[0-9][0-9]:[0-9][0-9]:[0-9][0-9]`
 Rmicroseconds = `\.[0-9][0-9][0-9][0-9][0-9][0-9]`
 Rline   = `(57|59):` // must update if the calls to l.Printf / l.Print below move
 Rlongfile  = `.*/[A-Za-z0-9_\-]+\.go:` + Rline
 Rshortfile = `[A-Za-z0-9_\-]+\.go:` + Rline
)

// ...

var tests = []tester{
 // individual pieces:
 {0, "", ""},
 {0, "XXX", "XXX"},
 {Ldate, "", Rdate + " "},
 {Ltime, "", Rtime + " "},
 {Ltime | Lmicroseconds, "", Rtime + Rmicroseconds + " "},
 {Lmicroseconds, "", Rtime + Rmicroseconds + " "}, // microsec implies time
 {Llongfile, "", Rlongfile + " "},
 {Lshortfile, "", Rshortfile + " "},
 {Llongfile | Lshortfile, "", Rshortfile + " "}, // shortfile overrides longfile
 // everything at once:
 {Ldate | Ltime | Lmicroseconds | Llongfile, "XXX", "XXX" + Rdate + " " + Rtime + Rmicroseconds + " " + Rlongfile + " "},
 {Ldate | Ltime | Lmicroseconds | Lshortfile, "XXX", "XXX" + Rdate + " " + Rtime + Rmicroseconds + " " + Rshortfile + " "},
}

和普通的Golang源代碼一樣,測試代碼中也能定義init函數,init函數會在引入外部包、定義常量、聲明變量之后被自動調用,可以在init函數里編寫測試相關的初始化代碼。

# /usr/local/go/src/bytes/buffer_test.go
func init() {
 testBytes = make([]byte, N)
 for i := 0; i  N; i++ {
  testBytes[i] = 'a' + byte(i%26)
 }
 data = string(testBytes)
}

封裝測試專用的公共函數,抽象測試專用的結構體等:

# /usr/local/go/src/log/log_test.go:
type tester struct {
 flag int
 prefix string
 pattern string // regexp that log output must match; we add ^ and expected_text$ always
}

// ...

func testPrint(t *testing.T, flag int, prefix string, pattern string, useFormat bool) {
 // ...
}

示例函數

示例函數無需接收參數,但需要使用注釋的 Output: 標記說明示例函數的輸出值,未指定Output:標記或輸出值為空的示例函數不會被執行。

示例函數需要歸屬于某個 包/函數/類型/類型 的方法,具體命名規則如下:

func Example() { ... }  # 包的示例函數
func ExampleF() { ... }  # 函數F的示例函數
func ExampleT() { ... }  # 類型T的示例函數
func ExampleT_M() { ... } # 類型T的M方法的示例函數

# 多示例函數 需要跟下劃線加小寫字母開頭的后綴
func Example_suffix() { ... }
func ExampleF_suffix() { ... }
func ExampleT_suffix() { ... }
func ExampleT_M_suffix() { ... }

go doc 工具會解析示例函數的函數體作為對應 包/函數/類型/類型的方法 的用法。

測試函數的相關說明,可以通過go help testfunc來查看幫助文檔。

使用 go test 工具

Golang中通過命令行工具go test來執行測試代碼,打開shell終端,進入需要測試的包所在的目錄執行 go test,或者直接執行go test $pkg_name_in_gopath即可對指定的包執行測試。

通過形如go test github.com/tabalt/...的命令可以執行$GOPATH/github.com/tabalt/目錄下所有的項目的測試。go test std命令則可以執行Golang標準庫的所有測試。

如果想查看執行了哪些測試函數及函數的執行結果,可以使用-v參數:

[tabalt@localhost hello] go test -v
=== RUN TestHello
--- PASS: TestHello (0.00s)
=== RUN ExampleHello
--- PASS: ExampleHello (0.00s)
PASS
ok  hello 0.006s

假設我們有很多功能測試函數,但某次測試只想執行其中的某一些,可以通過-run參數,使用正則表達式來匹配要執行的功能測試函數名。如下面指定參數后,功能測試函數TestHello不會執行到。

[tabalt@localhost hello] go test -v -run=xxx
PASS
ok  hello 0.006s

性能測試函數默認并不會執行,需要添加-bench參數,并指定匹配性能測試函數名的正則表達式;例如,想要執行某個包中所有的性能測試函數可以添加參數-bench . 或 -bench=.。

[tabalt@localhost hello] go test -bench=.
PASS
BenchmarkHello-8  2000000   657 ns/op
ok  hello 1.993s

想要查看性能測試時的內存情況,可以再添加參數-benchmem:

[tabalt@localhost hello] go test -bench=. -benchmem
PASS
BenchmarkHello-8  2000000   666 ns/op   208 B/op   9 allocs/op
ok  hello 2.014s

參數-cover可以用來查看我們編寫的測試對代碼的覆蓋率:


詳細的覆蓋率信息,可以通過-coverprofile輸出到文件,并使用go tool cover來查看,用法請參考go tool cover -help

更多go test命令的參數及用法,可以通過go help testflag來查看幫助文檔。

高級測試技術

IO相關測試

testing/iotest包中實現了常用的出錯的Reader和Writer,可供我們在io相關的測試中使用。主要有:

觸發數據錯誤dataErrReader,通過DataErrReader()函數創建

讀取一半內容的halfReader,通過HalfReader()函數創建

讀取一個byte的oneByteReader,通過OneByteReader()函數創建

觸發超時錯誤的timeoutReader,通過TimeoutReader()函數創建

寫入指定位數內容后停止的truncateWriter,通過TruncateWriter()函數創建

讀取時記錄日志的readLogger,通過NewReadLogger()函數創建

寫入時記錄日志的writeLogger,通過NewWriteLogger()函數創建

黑盒測試

testing/quick包實現了幫助黑盒測試的實用函數 Check和CheckEqual。

Check函數的第1個參數是要測試的只返回bool值的黑盒函數f,Check會為f的每個參數設置任意值并多次調用,如果f返回false,Check函數會返回錯誤值 *CheckError。Check函數的第2個參數 可以指定一個quick.Config類型的config,傳nil則會默認使用quick.defaultConfig。quick.Config結構體包含了測試運行的選項。

# /usr/local/go/src/math/big/int_test.go
func checkMul(a, b []byte) bool {
 var x, y, z1 Int
 x.SetBytes(a)
 y.SetBytes(b)
 z1.Mul(x, y)

 var z2 Int
 z2.SetBytes(mulBytes(a, b))

 return z1.Cmp(z2) == 0
}

func TestMul(t *testing.T) {
 if err := quick.Check(checkMul, nil); err != nil {
  t.Error(err)
 }
}

CheckEqual函數是比較給定的兩個黑盒函數是否相等,函數原型如下:

func CheckEqual(f, g interface{}, config *Config) (err error)

HTTP測試

net/http/httptest包提供了HTTP相關代碼的工具,我們的測試代碼中可以創建一個臨時的httptest.Server來測試發送HTTP請求的代碼:

ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
 fmt.Fprintln(w, "Hello, client")
}))
defer ts.Close()

res, err := http.Get(ts.URL)
if err != nil {
 log.Fatal(err)
}

greeting, err := ioutil.ReadAll(res.Body)
res.Body.Close()
if err != nil {
 log.Fatal(err)
}

fmt.Printf("%s", greeting)

還可以創建一個應答的記錄器httptest.ResponseRecorder來檢測應答的內容:

handler := func(w http.ResponseWriter, r *http.Request) {
 http.Error(w, "something failed", http.StatusInternalServerError)
}

req, err := http.NewRequest("GET", "http://example.com/foo", nil)
if err != nil {
 log.Fatal(err)
}

w := httptest.NewRecorder()
handler(w, req)

fmt.Printf("%d - %s", w.Code, w.Body.String())

測試進程操作行為

當我們被測函數有操作進程的行為,可以將被測程序作為一個子進程執行測試。下面是一個例子:

//被測試的進程退出函數
func Crasher() {
 fmt.Println("Going down in flames!")
 os.Exit(1)
}

//測試進程退出函數的測試函數
func TestCrasher(t *testing.T) {
 if os.Getenv("BE_CRASHER") == "1" {
  Crasher()
  return
 }
 cmd := exec.Command(os.Args[0], "-test.run=TestCrasher")
 cmd.Env = append(os.Environ(), "BE_CRASHER=1")
 err := cmd.Run()
 if e, ok := err.(*exec.ExitError); ok  !e.Success() {
  return
 }
 t.Fatalf("process ran with err %v, want exit status 1", err)
}

總結

以上就是這篇文章的全部內容了,希望本文的內容對大家學習或者使用Go語言能有所幫助,如果有疑問大家可以留言交流。

您可能感興趣的文章:
  • 深入理解Golang的單元測試和性能測試

標簽:紹興 宜昌 盤錦 邢臺 上海 雅安 眉山 七臺河

巨人網絡通訊聲明:本文標題《一步步教你編寫可測試的Go語言代碼》,本文關鍵詞  一,步步,教你,編寫,可,測試,;如發現本文內容存在版權問題,煩請提供相關信息告之我們,我們將及時溝通與處理。本站內容系統采集于網絡,涉及言論、版權與本站無關。
  • 相關文章
  • 下面列出與本文章《一步步教你編寫可測試的Go語言代碼》相關的同類信息!
  • 本頁收集關于一步步教你編寫可測試的Go語言代碼的相關信息資訊供網民參考!
  • 推薦文章
    欧美阿v视频在线大全_亚洲欧美中文日韩V在线观看_www性欧美日韩欧美91_亚洲欧美日韩久久精品
  • <rt id="w000q"><acronym id="w000q"></acronym></rt>
  • <abbr id="w000q"></abbr>
    <rt id="w000q"></rt>
    亚洲线精品一区二区三区| 国产精品免费在线视频| 亚洲人一二三区| 中文字幕在线观看一区二区| 国产精品麻豆久久久| 亚洲私人黄色宅男| 一区二区免费视频| 香蕉久久一区二区不卡无毒影院 | 在线不卡中文字幕播放| 欧美性一二三区| 在线成人免费观看| 久久天堂av综合合色蜜桃网| 亚洲国产激情av| 亚洲女同一区二区| 性做久久久久久久免费看| 蜜芽一区二区三区| 国产精品一区在线观看乱码| av电影在线观看一区| 中文字幕久久久久久久| 久久丫精品国产亚洲av不卡| 97人妻人人揉人人躁人人| 一本久道久久综合中文字幕 | 国产精品无码一区二区三区免费| www.日本高清视频| 欧美系列日韩一区| 久久一夜天堂av一区二区三区| 日韩美女啊v在线免费观看| 午夜精品久久久久久久蜜桃app| 韩国欧美一区二区| 4438x全国最大成人| 国产精品久久久久久成人| 伊人av在线播放| 亚洲欧美综合7777色婷婷| 欧美日韩精品一区二区天天拍小说| 337p粉嫩大胆色噜噜噜噜亚洲| 亚洲少妇中出一区| 国产制服丝袜一区| 免费的av网站| 欧美日韩综合色| 中文字幕亚洲成人| 韩国三级中文字幕hd久久精品| 国模私拍在线观看| 在线观看日韩国产| 亚洲欧洲无码一区二区三区| 九九国产精品视频| 日本黄色网址大全| 91麻豆精品国产91久久久久久久久| 国产精品国产a| 国产成人免费视频网站 | 久久国产生活片100| 一区二区三区免费在线观看视频| 欧洲精品一区二区三区在线观看| 国产精品乱子久久久久| 国产在线一区二区综合免费视频| 人妻丰满熟妇aⅴ无码| 91精品国产一区二区| 亚洲成人777| 国产+高潮+白浆+无码| 制服.丝袜.亚洲.中文.综合| 亚洲国产一二三| 久久久午夜精品福利内容| 欧美一区二区三区的| 日本欧美在线看| 国产真实乱人偷精品人妻| 久久伊人中文字幕| 国产成a人亚洲| 91久久久免费一区二区| 一区二区三区四区在线| 中文字幕天堂av| 日韩免费看网站| 国产酒店精品激情| 一本大道av伊人久久综合| 亚洲欧美视频在线观看| 精品视频站长推荐| 久久精品亚洲乱码伦伦中文 | 日韩黄色免费电影| 快灬快灬一下爽蜜桃在线观看| 国产无人区一区二区三区| av不卡免费电影| 日韩美女在线视频| 成人妖精视频yjsp地址| 91福利在线看| 狠狠色丁香婷婷综合| 日本精品免费观看高清观看| 午夜伦理一区二区| 侵犯稚嫩小箩莉h文系列小说| 亚洲午夜久久久久| 岛国片在线免费观看| 亚洲国产欧美在线| 欧美日韩国产黄色| 亚洲成人777| 欧美性淫爽ww久久久久无| 黑人精品欧美一区二区蜜桃| 欧美日韩一卡二卡| 国产一区二区不卡在线 | 国产一区二区h| 337p亚洲精品色噜噜噜| 成人短视频下载| 精品久久久久久综合日本欧美| 91在线码无精品| 中文字幕成人在线观看| 亚洲天堂久久新| 午夜欧美电影在线观看| 69av.com| 国产成人高清视频| 99久久综合精品| 我不卡一区二区| 免费在线观看视频一区| 欧美日韩第一区日日骚| 成人av在线一区二区三区| 国产欧美一区二区精品性色超碰| 日韩www视频| 日韩av电影免费观看高清完整版| 色94色欧美sute亚洲13| caoporm超碰国产精品| 国产精品欧美极品| 四虎地址8848| 国产91综合网| 亚洲婷婷综合色高清在线| 一区二区国产精品精华液| 成人手机在线视频| 亚洲色图制服丝袜| 欧美午夜影院一区| 绯色av蜜臀vs少妇| 午夜久久久影院| 欧美xxxx在线观看| 日韩a级片在线观看| 国产女主播在线播放| 香蕉加勒比综合久久| 欧美日韩免费电影| 成人午夜电影久久影院| 国产午夜亚洲精品理论片色戒| 日本一区二区三区网站| 婷婷中文字幕一区三区| 国产精品久久毛片av大全日韩| 精品无码一区二区三区蜜臀| 精品成人在线观看| 性欧美一区二区| 成人精品视频一区二区三区| 亚洲欧洲另类国产综合| 精品视频在线视频| 日韩乱码人妻无码中文字幕久久| 国产自产2019最新不卡| 亚洲日穴在线视频| 欧美一区二区在线视频| 婷婷国产成人精品视频| 少妇极品熟妇人妻无码| 另类人妖一区二区av| 亚洲色图在线播放| 欧美性猛交xxxx黑人交| 91亚洲精品久久久蜜桃| 免费在线观看视频一区| 亚洲免费伊人电影| 精品99久久久久久| 欧美日韩一区二区在线观看| 蜜桃久久精品成人无码av| wwwww在线观看| 国产成都精品91一区二区三| 日本欧美大码aⅴ在线播放| 亚洲天堂2014| 欧美国产综合一区二区| 日韩精品一区二区三区在线观看| 亚洲日本va午夜在线影院| 亚洲欧洲久久久| 午夜免费福利影院| 久久久久亚洲av片无码v| 国产精品一二三区| 美国十次综合导航| 性久久久久久久久| 夜夜爽夜夜爽精品视频| 亚洲成人生活片| 日本一级片免费| 免费看黄色av| 国产肥白大熟妇bbbb视频| 日本黄色动态图| 日本japanese极品少妇| a级一a一级在线观看| 国产chinese中国hdxxxx| 性生活一级大片| 日韩av成人网| 无码国产69精品久久久久网站| 黑人性生活视频| 成人欧美精品一区二区| 久久精品aⅴ无码中文字字幕重口| 91麻豆自制传媒国产之光| 麻豆网站免费观看| 日韩精品视频一区二区| 国产黄色三级网站| 日本少妇色视频| 性欧美一区二区| 色综合激情久久| 欧美日韩国产另类一区| 欧美一区二区三区不卡| 亚洲精品在线三区| 国产精品私人影院| 一区二区三区日韩精品视频| 五月激情六月综合| 狠狠狠色丁香婷婷综合久久五月| 国产综合色视频| 成人爽a毛片一区二区免费|