<kbd id="afajh"><form id="afajh"></form></kbd>
<strong id="afajh"><dl id="afajh"></dl></strong>
    <del id="afajh"><form id="afajh"></form></del>
        1. <th id="afajh"><progress id="afajh"></progress></th>
          <b id="afajh"><abbr id="afajh"></abbr></b>
          <th id="afajh"><progress id="afajh"></progress></th>

          Golang單元測(cè)試

          共 10455字,需瀏覽 21分鐘

           ·

          2021-08-14 06:01

          目錄

          • 1、單元測(cè)試概述

            • 1.1 什么是單元&單元測(cè)試

            • 1.2 為什么進(jìn)行單元測(cè)試

            • 1.3 單元測(cè)試用例編寫的原則

            • 1.4 單測(cè)用例規(guī)定

          • 2、golang 常用的單測(cè)框架

            • 2.1 testing

            • 2.2 goconvey

            • 2.3 testify



          1、單元測(cè)試概述

          1.1 什么是單元&單元測(cè)試

          • 單元是應(yīng)用的最小可測(cè)試部件,如函數(shù)和對(duì)象的方法
          • 單元測(cè)試是軟件開發(fā)中對(duì)最小單位進(jìn)行正確性檢驗(yàn)的測(cè)試工作

          1.2 為什么進(jìn)行單元測(cè)試

          • 保證變更/重構(gòu)的正確性,特別是在一些頻繁變動(dòng)和多人合作開發(fā)的項(xiàng)目中
          • 簡化調(diào)試過程:可以輕松的讓我們知道哪一部分代碼出了問題
          • 單測(cè)最好的文檔:在單測(cè)中直接給出具體接口的使用方法,是最好的實(shí)例代碼

          1.3 單元測(cè)試用例編寫的原則

          • 單一原則:一個(gè)測(cè)試用例只負(fù)責(zé)一個(gè)場(chǎng)景
          • 原子性:結(jié)果只有兩種情況:PassFail
          • 優(yōu)先要核心組件和邏輯的測(cè)試用例
          • 高頻使用庫,util,重點(diǎn)覆蓋

          1.4 單測(cè)用例規(guī)定

          • 文件名必須要xx_test.go命名
          • 測(cè)試方法必須是TestXXX開頭
          • 方法中的參數(shù)必須是t *testing.T
          • 測(cè)試文件和被測(cè)試文件必須在一個(gè)包中

          2、golang 常用的單測(cè)框架

          2.1 testing

          https://golang.google.cn/pkg/testing/

          2.1.1 單元測(cè)試

          Go提供了test工具用于代碼的單元測(cè)試,test工具會(huì)查找包下以_test.go結(jié)尾的文件,調(diào)用測(cè)試文件中以 TestBenchmark開頭的函數(shù)并給出運(yùn)行結(jié)果

          測(cè)試函數(shù)需要導(dǎo)入testing包,并定義以Test開頭的函數(shù),參數(shù)為testing.T指針類型,在測(cè)試函數(shù)中調(diào)用函數(shù)進(jìn)行返回值測(cè)試,當(dāng)測(cè)試失敗可通過testing.T結(jié)構(gòu)體的Error函數(shù)拋出錯(cuò)誤

          單元測(cè)試是對(duì)某個(gè)功能的測(cè)試 命令行執(zhí)行

          go test 包名  # 測(cè)試整個(gè)包
          go test -v .
          go test 包名/文件名  # 測(cè)試某個(gè)文件

          簡單使用 準(zhǔn)備待測(cè)代碼compute.go

          package pkg03

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

          func Mul(a, b int) int {
           return a * b
          }

          func Div(a, b int) int {
           return a / b
          }

          準(zhǔn)備測(cè)試用例compute_test.go

          package pkg03

          import "testing"

          func TestAdd(t *testing.T) {
           a := 10
           b := 20
           want := 30
           actual := Add(a, b)
           if want != actual {
            t.Errorf("Add函數(shù)參數(shù):%d %d, 期望: %d, 實(shí)際: %d", a, b, want, actual)
           }
          }

          func TestMul(t *testing.T) {
           a := 10
           b := 20
           want := 300
           actual := Mul(a, b)
           if want != actual {
            t.Errorf("Mul函數(shù)參數(shù):%d %d, 期望: %d, 實(shí)際: %d", a, b, want, actual)
           }
          }

          func TestDiv(t *testing.T) {
           a := 10
           b := 20
           want := 2
           actual := Div(a, b)
           if want != actual {
            t.Errorf("Div函數(shù)參數(shù):%d %d, 期望: %d, 實(shí)際: %d", a, b, want, actual)
           }
          }

          執(zhí)行測(cè)試

          ?  pwd
          golang-learning/chapter06/pkg03
          ?  go test -v .
          === RUN   TestAdd
          --- PASS: TestAdd (0.00s)
          === RUN   TestMul
              compute_test.go:21: Mul函數(shù)參數(shù):10 20, 期望: 300, 實(shí)際: 200
          --- FAIL: TestMul (0.00s)
          === RUN   TestDiv
              compute_test.go:31: Div函數(shù)參數(shù):10 20, 期望: 2, 實(shí)際: 0
          --- FAIL: TestDiv (0.00s)
          FAIL
          FAIL    pkg03   0.198s
          FAIL

          只執(zhí)行某個(gè)函數(shù)

          go test -run=TestAdd -v .
          === RUN   TestAdd
          --- PASS: TestAdd (0.00s)
          PASS
          ok      pkg03   0.706s

          正則過濾函數(shù)名

          go test -run=TestM.* -v .

          2.1.2 測(cè)試覆蓋率

          用于統(tǒng)計(jì)目標(biāo)包有百分之多少的代碼參與了單測(cè) 使用go test工具進(jìn)行單元測(cè)試并將測(cè)試覆蓋率覆蓋分析結(jié)果輸出到cover.out文件

          例如上面的例子

          go test -v -cover
          === RUN   TestAdd
          --- PASS: TestAdd (0.00s)
          === RUN   TestMul
              compute_test.go:21: Mul函數(shù)參數(shù):10 20, 期望: 300, 實(shí)際: 200
          --- FAIL: TestMul (0.00s)
          === RUN   TestDiv
              compute_test.go:31: Div函數(shù)參數(shù):10 20, 期望: 2, 實(shí)際: 0
          --- FAIL: TestDiv (0.00s)
          FAIL
          coverage: 100.0% of statements
          exit status 1
          FAIL    pkg03   0.185s

          生成測(cè)試覆蓋率文件

          go test -v -coverprofile=cover.out
          === RUN   TestAdd
          --- PASS: TestAdd (0.00s)
          === RUN   TestAddFlag
          --- PASS: TestAddFlag (0.00s)
          PASS
          coverage: 75.0% of statements
          ok      testcalc/calc   0.960s

          分析測(cè)試結(jié)果,打開測(cè)試覆蓋率結(jié)果文件,查看測(cè)試覆蓋率

          go tool cover -html cover.out

          2.1.3 子測(cè)試 t.run

          func TestMul2(t *testing.T) {
           t.Run("正數(shù)"func(t *testing.T) {
            if Mul(45) != 20 {
             t.Fatal("muli.zhengshu.error")
            }
           })
           t.Run("負(fù)數(shù)"func(t *testing.T) {
            if Mul(2-3) != -6 {
             t.Fatal("muli.fushu.error")
            }
           })
          }

          執(zhí)行測(cè)試

          ?  go test -v .
          === RUN   TestMul2
          === RUN   TestMul2/正數(shù)
          === RUN   TestMul2/負(fù)數(shù)
          --- PASS: TestMul2 (0.00s)
              --- PASS: TestMul2/正數(shù) (0.00s)
              --- PASS: TestMul2/負(fù)數(shù) (0.00s)

          指定func/sub運(yùn)行子測(cè)試

          ?  go test -run=TestMul2/正數(shù) -v
          === RUN   TestMul2
          === RUN   TestMul2/正數(shù)
          --- PASS: TestMul2 (0.00s)
              --- PASS: TestMul2/正數(shù) (0.00s)
          PASS
          ok      pkg03   0.675s

          子測(cè)試的作用:table-driven tests

          • 所有用例的數(shù)據(jù)組織在切片cases中,看起來就像一張表,借助循環(huán)創(chuàng)建子測(cè)試。這樣寫的好處有

            • 新增用例非常簡單,只需給cases新增一條測(cè)試數(shù)據(jù)即可
            • 測(cè)試代碼可讀性好,直觀地能夠看到每個(gè)子測(cè)試的參數(shù)和期待的返回值
            • 用例失敗時(shí),報(bào)錯(cuò)信息的格式比較統(tǒng)一,測(cè)試報(bào)告易于閱讀
            • 如果數(shù)據(jù)量較大,或是一些二進(jìn)制數(shù)據(jù),推薦使用相對(duì)路徑從文件中讀取
          • 舉例:prometheus 源碼[1]

          2.2 goconvey

          goconvey是一個(gè)第三方測(cè)試框架,其最大好處就是對(duì)常規(guī)的if else進(jìn)行了高度封裝

          2.2.1 基本使用

          準(zhǔn)備待測(cè)代碼student.go

          package pkg04

          import "fmt"

          type Student struct {
           Name      string
           ChiScore  int
           EngScore  int
           MathScore int
          }

          func NewStudent(name string) (*Student, error) {
           if name == "" {
            return nil, fmt.Errorf("name為空")
           }
           return &Student{
            Name: name,
           }, nil
          }

          func (s *Student) GetAvgScore() (int, error) {
           score := s.ChiScore + s.EngScore + s.MathScore
           if score == 0 {
            return 0, fmt.Errorf("全都是0分")
           }
           return score / 3nil
          }

          參考官方示例,準(zhǔn)備測(cè)試用例student_test.go直觀來講,使用goconvey的好處是不用再寫多個(gè)if判斷

          package pkg04

          import (
           . "github.com/smartystreets/goconvey/convey"
           "testing"
          )

          func TestNewStudent(t *testing.T) {
           Convey("start test new", t, func() {
            stu, err := NewStudent("")
            Convey("空的name初始化錯(cuò)誤"func() {
             So(err, ShouldBeError)
            })
            Convey("stu對(duì)象為nil"func() {
             So(stu, ShouldBeNil)
            })
           })
          }

          func TestScore(t *testing.T) {
           stu, _ := NewStudent("hh")
           Convey("不設(shè)置分?jǐn)?shù)可能出錯(cuò)", t, func() {
            sc, err := stu.GetAvgScore()
            Convey("獲取分?jǐn)?shù)出錯(cuò)了"func() {
             So(err, ShouldBeError)
            })
            Convey("分?jǐn)?shù)為0"func() {
             So(sc, ShouldEqual, 0)
            })
           })
           Convey("正常情況", t, func() {
            stu.ChiScore = 60
            stu.EngScore = 70
            stu.MathScore = 80
            score, err := stu.GetAvgScore()
            Convey("獲取分?jǐn)?shù)出錯(cuò)了"func() {
             So(err, ShouldBeNil)
            })
            Convey("平均分大于60"func() {
             So(score, ShouldBeGreaterThan, 60)
            })
           })
          }

          執(zhí)行go test -v .

          ?  go test -v .
          === RUN   TestNewStudent

            start test new
              空的name初始化錯(cuò)誤 ?
              stu對(duì)象為nil ?


          2 total assertions

          --- PASS: TestNewStudent (0.00s)
          === RUN   TestScore

            不設(shè)置分?jǐn)?shù)可能出錯(cuò)
              獲取分?jǐn)?shù)出錯(cuò)了 ?
              分?jǐn)?shù)為0 ?


          4 total assertions


            正常情況
              獲取分?jǐn)?shù)出錯(cuò)了 ?
              平均分大于60 ?


          6 total assertions

          --- PASS: TestScore (0.00s)
          PASS
          ok      pkg04   0.126s

          2.2.2 圖形化使用

          • 確保本地有goconvey的二進(jìn)制
          go get github.com/smartystreets/goconvey
          # 會(huì)將對(duì)應(yīng)的二進(jìn)制文件放到 $GOPATH/bin 下面
          • 編輯環(huán)境變量把GOPATH/bin加入PATH里面 或者寫全路徑
          • 到測(cè)試的目錄下,執(zhí)行goconvey,啟動(dòng)http 8000,自動(dòng)運(yùn)行測(cè)試用例
          • 瀏覽器訪問 http://127.0.0.1:8000

          最終效果如下

          2.3 testify

          2.3.1 簡單使用

          業(yè)務(wù)代碼cal.go

          package pkg05

          func Add(x int ) (result int) {
           result = x + 2
           return result
          }

          測(cè)試用例cal_test.go

          package pkg05

          import (
           "github.com/stretchr/testify/assert"
           "testing"
          )

          func TestAdd(t *testing.T) {
           // assert equality
           assert.Equal(t, Add(5), 7"they should be equal")
          }

          執(zhí)行測(cè)試

          ?  go test -v .
          === RUN   TestAdd
          --- PASS: TestAdd (0.00s)
          PASS
          ok      pkg05   1.216s

          2.3.2 表驅(qū)動(dòng)測(cè)試

          package pkg05

          import (
           "github.com/stretchr/testify/assert"
           "testing"
          )

          func TestAdd(t *testing.T) {
           // assert equality
           assert.Equal(t, Add(5), 7"they should be equal")
          }

          func TestCal(t *testing.T) {
           ass := assert.New(t)
           var tests = []struct {
            input    int
            expected int
           }{
            {24},
            {-11},
            {02},
            {-5-3},
            {999999997999999999},
           }
           for _, test := range tests {
            ass.Equal(Add(test.input), test.expected)
           }
          }

          2.3.3 mock 功能

          • 使用testify/mock隔離第三方依賴或者復(fù)雜調(diào)用
          • testfiy/mock使得偽造對(duì)象的輸入輸出值可以在運(yùn)行時(shí)決定
          • 參考:https://github.com/euclidr/testingo

          2.3.4 單元測(cè)試覆蓋率應(yīng)用實(shí)例

          https://github.com/m3db/m3/pull/3525

          參考資料

          [1]

          prometheus 源碼:https://github.com/prometheus/prometheus/blob/main/web/api/v1/api_test.go: https://github.com/prometheus/prometheus/blob/main/web/api/v1/api_test.go

          瀏覽 79
          點(diǎn)贊
          評(píng)論
          收藏
          分享

          手機(jī)掃一掃分享

          分享
          舉報(bào)
          評(píng)論
          圖片
          表情
          推薦
          點(diǎn)贊
          評(píng)論
          收藏
          分享

          手機(jī)掃一掃分享

          分享
          舉報(bào)
          <kbd id="afajh"><form id="afajh"></form></kbd>
          <strong id="afajh"><dl id="afajh"></dl></strong>
            <del id="afajh"><form id="afajh"></form></del>
                1. <th id="afajh"><progress id="afajh"></progress></th>
                  <b id="afajh"><abbr id="afajh"></abbr></b>
                  <th id="afajh"><progress id="afajh"></progress></th>
                  www.8x8x | 伊人网成人 | 日比视频免费 | 亚洲中文字幕在线视频 | 91亚洲成人 |