溫馨提示×

溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊×
其他方式登錄
點擊 登錄注冊 即表示同意《億速云用戶服務(wù)條款》

Golang模糊測試工具如何使用

發(fā)布時間:2023-03-06 11:32:02 來源:億速云 閱讀:101 作者:iii 欄目:開發(fā)技術(shù)

本篇內(nèi)容介紹了“Golang模糊測試工具如何使用”的有關(guān)知識,在實際案例的操作過程中,不少人都會遇到這樣的困境,接下來就讓小編帶領(lǐng)大家學(xué)習(xí)一下如何處理這些情況吧!希望大家仔細閱讀,能夠?qū)W有所成!

開發(fā)環(huán)境

升級到Go 1.18
Go 1.18雖然還沒正式發(fā)布,但可以下載RC版本,而且即使你生產(chǎn)環(huán)境用是Go的老版本,你個人的本地開發(fā)環(huán)境也可以升級到1.18,還可以使用go-fuzzing更好的自測

go-fuzzing

官方文檔:go fuzzing是通過持續(xù)給一個程序不同的輸入來自動化測試,并通過分析代碼覆蓋率來智能的尋找失敗的例子。這種方法可以盡可能的找到一些邊界問題,親測確實發(fā)現(xiàn)的都是些平時比較難發(fā)現(xiàn)的問題。

fuzzing,又叫fuzz testing,中文叫做模糊測試或隨機測試。其本質(zhì)上是一種自動化測試技術(shù),更具體一點,它是一種基于隨機輸入的自動化測試技術(shù),常被用于發(fā)現(xiàn)處理用戶輸入的代碼中存在的bug和問題。

fuzz tests規(guī)則

func FuzzFoo(f *testing.F) {
    f.Add(5, "hello")

    f.Fuzz(func(t *testing.T, i int, s string) {
        out, err := Foo(i, s)
        if err != nil && out != "" {
            t.Errorf("%q, %v", out, err)
        }
    })
}
  • 函數(shù)必須是Fuzz開頭,唯一的參數(shù)只有*testing.F,沒有返回值

  • Fuzz tests必須在名為*_test.go的文件下才能執(zhí)行

  • fuzz target是個方法,它調(diào)用(*testing.F).Fuzz,第一個參數(shù)是 *testing.T,之后的參數(shù)就是稱之為fuzzing arguments的參數(shù),方法沒有返回值

  • 每個fuzz test中只能有一個fuzz target

  • 調(diào)用f.Add()的時候需要參數(shù)類型跟fuzzing arguments順序和類型都保持一致

  • fuzzing arguments只支持以下類型:

    • int, int8, int16, int32/rune, int64

    • uint, uint8/byte, uint16, uint32, uint64

    • string, []byte

    • float32, float64

    • bool

如何使用go-fuzzing

1、首先要先定義fuzzing arguments,并通過fuzzing arguments寫fuzzing target

2、思考fuzzing target怎么寫,重點是怎么驗證結(jié)果的正確性,因為fuzzing arguments是隨機給的,所以要有個驗證結(jié)果的方法

3、遇到失敗的例子怎么去打印出錯誤結(jié)果

4、根據(jù)錯誤結(jié)果去生成新的測試用例,這個新的測試用例會被用來調(diào)試發(fā)現(xiàn)的bug,并且可以留下給CI使用

下面是一個切片中數(shù)字求和的例子:

// slice_sum.go
func SliceSum(arr []int64) int64 {
  var sum int64

  for _, val := range arr {
    if val % 100000 != 0 {
      sum += val
    }
  }

  return sum
}

第一步:定義fuzzing arguments模糊參數(shù)

至少需要給出一個fuzzing arguments,不然go-fuzzing沒法生成測試代碼。
這是切片中元素求和的方法,那我們可以把切片的元素個數(shù)n(自行模擬個數(shù)即可)作為fuzzing arguments,然后go-fuzzing會根據(jù)運行的代碼覆蓋率自動生成不同的參數(shù)來模擬測試。

// slice_sum_test.go
func FuzzSliceSum(f *testing.F) {
  // 10,go-fuzzing稱之為語料,10這個值就是讓go fuzzing冷啟動的一個值,具體多少不重要
  f.Add(10)

  f.Fuzz(func(t *testing.T, n int) {
      // 限制20個元素
    n %= 20

    // 剩余處理

  })
}

第二步:編寫fuzzing target

重點是編寫可以驗證的fuzzing target,不僅要根據(jù)給定的模糊參數(shù)寫出測試代碼,而且還需要生成可以驗證結(jié)果正確性的數(shù)據(jù)。
對這個切片元素求和的方法來說,就是隨機生成n個元素的切片,然后進行求和得到正確的結(jié)果。

package fuzz

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

// slice_sum_test.go
func FuzzSliceSum(f *testing.F) {
  // 初始化隨機數(shù)種子
  rand.Seed(time.Now().UnixNano())
  // 語料
  f.Add(10)

  f.Fuzz(func(t *testing.T, n int) {
    n %= 20

    var arr []int64
    var expect int64 // 期望值

    for i := 0; i < n; i++ {
      val := rand.Int63() % 1000000
      arr = append(arr, val)
      expect += val
    }

    // 自己求和的結(jié)果和調(diào)用函數(shù)求和的結(jié)果比對
    assert.Equal(t, expect, SliceSum(arr))
  })
}

執(zhí)行模糊測試

?  fuzz go test -fuzz=SliceSum
fuzz: elapsed: 0s, gathering baseline coverage: 0/52 completed
fuzz: elapsed: 0s, gathering baseline coverage: 52/52 completed, now fuzzing with 8 workers
fuzz: elapsed: 0s, execs: 9438 (34179/sec), new interesting: 2 (total: 54)
--- FAIL: FuzzSliceSum (0.28s)
    --- FAIL: FuzzSliceSum (0.00s)
        slice_sum_test.go:32: 
                Error Trace:    slice_sum_test.go:32
                                                        value.go:556
                                                        value.go:339
                                                        fuzz.go:337
                Error:          Not equal: 
                                expected: 5715923
                                actual  : 5315923
                Test:           FuzzSliceSum
    
    Failing input written to testdata/fuzz/FuzzSliceSum/8e8981ffa4ee4d93f475c807563f9d63854a6c913cdfb10a73191549318a2a51
    To re-run:
    go test -run=FuzzSliceSum/8e8981ffa4ee4d93f475c807563f9d63854a6c913cdfb10a73191549318a2a51
FAIL
exit status 1
FAIL    demo/fuzz       0.287s

上面這段輸出,你只能看出預(yù)期值和實際值不一樣,但是很難分析錯誤。

第三步:打印出錯誤的例子

上面的錯誤輸出,如果能打印出造成錯誤的例子的話,就可以直接作為測試用例進行單測。我們總不能一個個去試吧,而且錯誤的例子未必只有一個。

修改下模糊測試代碼,增加打?。?/p>

package fuzz

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

// slice_sum_test.go
func FuzzSliceSum(f *testing.F) {
    // 初始化隨機數(shù)種子
    rand.Seed(time.Now().UnixNano())
    // 語料
    f.Add(10)

    f.Fuzz(func(t *testing.T, n int) {
        n %= 20

        var arr []int64
        var expect int64 // 期望值
        var buf strings.Builder
        buf.WriteString("\n")

        for i := 0; i < n; i++ {
            val := rand.Int63() % 1000000
            arr = append(arr, val)
            expect += val
            buf.WriteString(fmt.Sprintf("%d,\n", val))
        }

        // 自己求和的結(jié)果和調(diào)用函數(shù)求和的結(jié)果比對
        assert.Equal(t, expect, SliceSum(arr), buf.String())
    })
}

再次執(zhí)行模糊測試

?  fuzz go test -fuzz=SliceSum
fuzz: elapsed: 0s, gathering baseline coverage: 0/47 completed
fuzz: elapsed: 0s, gathering baseline coverage: 47/47 completed, now fuzzing with 8 workers
fuzz: elapsed: 0s, execs: 17109 (42507/sec), new interesting: 2 (total: 49)
--- FAIL: FuzzSliceSum (0.41s)
    --- FAIL: FuzzSliceSum (0.00s)
        slice_sum_test.go:34: 
                Error Trace:    slice_sum_test.go:34
                                                        value.go:556
                                                        value.go:339
                                                        fuzz.go:337
                Error:          Not equal: 
                                expected: 7575516
                                actual  : 7175516
                Test:           FuzzSliceSum
                Messages:       
                                92016,
                                642504,
                                400000,
                                489403,
                                472011,
                                811028,
                                315130,
                                298207,
                                57765,
                                542614,
                                136594,
                                351360,
                                867104,
                                918715,
                                515092,
                                665973,
    
    Failing input written to testdata/fuzz/FuzzSliceSum/9191ba4d7ea5420a9a76661d4e7d6a7a4e69ad4d5d8ef306ff78161a2acf1416
    To re-run:
    go test -run=FuzzSliceSum/9191ba4d7ea5420a9a76661d4e7d6a7a4e69ad4d5d8ef306ff78161a2acf1416
FAIL
exit status 1
FAIL    demo/fuzz       0.413s

第四步:根據(jù)輸出的錯誤例子,編寫新的測試用例進行單測

// 單測通過后,再執(zhí)行模糊測試,看看有沒有其他邊緣問題出現(xiàn)
func TestSliceSumFuzzCase1(t *testing.T) {
    arr := []int64{
        92016,
        642504,
        400000,
        489403,
        472011,
        811028,
        315130,
        298207,
        57765,
        542614,
        136594,
        351360,
        867104,
        918715,
        515092,
        665973,
    }
    // 期望值從第三步的輸出中獲取
    assert.Equal(t, int64(7575516), SliceSum(arr))
}

這樣就可以很方便的進行調(diào)試了,并且能夠增加有效的測試用例進行單測,確保這個bug不會出現(xiàn)了。

生產(chǎn)環(huán)境項目Go版本問題

線上項目的Go版本不能升級到1.18怎么辦?

線上的版本不升級到1.18,但是我們本地開發(fā)升級沒有問題,可以在文件的頭部增加如下命令注釋:

slice_sum_test.go

//go:build go1.18
// +build go1.18

這樣我們在線上不管用哪個版本都不會報錯,而且我們一般都是在本地進行模糊測試

注意:第三行必須是空行,不然就會變成package的注釋了

有些還無法復(fù)現(xiàn)的問題,比如協(xié)程死鎖,輸出一直在執(zhí)行或者卡住然后過一會才結(jié)束,這類的長時間執(zhí)行的模糊測試,我還沒有摸透。如果有大佬知道的話麻煩也告訴我下。

“Golang模糊測試工具如何使用”的內(nèi)容就介紹到這里了,感謝大家的閱讀。如果想了解更多行業(yè)相關(guān)的知識可以關(guān)注億速云網(wǎng)站,小編將為大家輸出更多高質(zhì)量的實用文章!

向AI問一下細節(jié)

免責(zé)聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點不代表本網(wǎng)站立場,如果涉及侵權(quán)請聯(lián)系站長郵箱:is@yisu.com進行舉報,并提供相關(guān)證據(jù),一經(jīng)查實,將立刻刪除涉嫌侵權(quán)內(nèi)容。

AI