<em>Mac</em>Book项目 2009年学校开始实施<em>Mac</em>Book项目,所有师生配备一本<em>Mac</em>Book,并同步更新了校园无线网络。学校每周进行电脑技术更新,每月发送技术支持资料,极大改变了教学及学习方式。因此2011
2021-06-01 09:32:01
我們經常調侃程式設計師每天都在寫bug,這確實是事實,沒有測出bug不代表程式就真的不存在問題。傳統的程式碼review、靜態分析、人工測試和自動化的單元測試無法窮盡所有輸入組合,尤其是難以模擬一些隨機的、邊緣的資料。
去年6月,Go官方釋出稱gotip版本已經原生支援Fuzzing並開始了公測,將與[Go 1.18版本]一起在2022年中釋出,go-fuzzing至今已經發現了Go標準庫超過200個bug(https://github.com/dvyukov/go-fuzz )。即將釋出的[Go 1.18版本]就提供了一個程式碼自測的絕佳工具go-fuzzing。
說到[Go 1.18版本],大家最關注的應該是泛型,但是我個人覺得go-fuzzing也是其中的一個亮點,Go 1.18將fuzz testing納入了go test工具鏈,與單元測試、效能基準測試等一起成為了Go原生測試工具鏈中的重要成員。
本次就來說下go-fuzzing這個工具。
升級到Go 1.18
Go 1.18雖然還沒正式釋出,但可以下載RC版本,而且即使你生產環境用是Go的老版本,你個人的本地開發環境也可以升級到1.18,還可以使用go-fuzzing更好的自測
官方檔案:go fuzzing是通過持續給一個程式不同的輸入來自動化測試,並通過分析程式碼覆蓋率來智慧的尋找失敗的例子。這種方法可以儘可能的找到一些邊界問題,親測確實發現的都是些平時比較難發現的問題。
fuzzing,又叫fuzz testing,中文叫做模糊測試或隨機測試。其本質上是一種自動化測試技術,更具體一點,它是一種基於隨機輸入的自動化測試技術,常被用於發現處理使用者輸入的程式碼中存在的bug和問題。
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) } }) }
1、首先要先定義fuzzing arguments,並通過fuzzing arguments寫fuzzing target
2、思考fuzzing target怎麼寫,重點是怎麼驗證結果的正確性,因為fuzzing arguments是隨機給的,所以要有個驗證結果的方法
3、遇到失敗的例子怎麼去列印出錯誤結果
4、根據錯誤結果去生成新的測試用例,這個新的測試用例會被用來偵錯發現的bug,並且可以留下給CI使用
下面是一個切片中數位求和的例子:
// 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模糊引數
至少需要給出一個fuzzing arguments,不然go-fuzzing沒法生成測試程式碼。
這是切片中元素求和的方法,那我們可以把切片的元素個數n(自行模擬個數即可)作為fuzzing arguments,然後go-fuzzing會根據執行的程式碼覆蓋率自動生成不同的引數來模擬測試。
// 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,不僅要根據給定的模糊引數寫出測試程式碼,而且還需要生成可以驗證結果正確性的資料。
對這個切片元素求和的方法來說,就是隨機生成n個元素的切片,然後進行求和得到正確的結果。
package fuzz import ( "github.com/stretchr/testify/assert" "math/rand" "testing" "time" ) // slice_sum_test.go func FuzzSliceSum(f *testing.F) { // 初始化亂數種子 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 } // 自己求和的結果和呼叫函數求和的結果比對 assert.Equal(t, expect, SliceSum(arr)) }) }
執行模糊測試
➜ 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
上面這段輸出,你只能看出預期值和實際值不一樣,但是很難分析錯誤。
第三步:列印出錯誤的例子
上面的錯誤輸出,如果能列印出造成錯誤的例子的話,就可以直接作為測試用例進行單測。我們總不能一個個去試吧,而且錯誤的例子未必只有一個。
修改下模糊測試程式碼,增加列印:
package fuzz import ( "github.com/stretchr/testify/assert" "math/rand" "testing" "time" ) // slice_sum_test.go func FuzzSliceSum(f *testing.F) { // 初始化亂數種子 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)) } // 自己求和的結果和呼叫函數求和的結果比對 assert.Equal(t, expect, SliceSum(arr), buf.String()) }) }
再次執行模糊測試
➜ 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
第四步:根據輸出的錯誤例子,編寫新的測試用例進行單測
// 單測通過後,再執行模糊測試,看看有沒有其他邊緣問題出現 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)) }
這樣就可以很方便的進行偵錯了,並且能夠增加有效的測試用例進行單測,確保這個bug不會出現了。
線上專案的Go版本不能升級到1.18怎麼辦?
線上的版本不升級到1.18,但是我們本地開發升級沒有問題,可以在檔案的頭部增加如下命令註釋:
slice_sum_test.go
//go:build go1.18 // +build go1.18
這樣我們線上上不管用哪個版本都不會報錯,而且我們一般都是在本地進行模糊測試
注意:第三行必須是空行,不然就會變成package的註釋了
有些還無法復現的問題,比如協程死鎖,輸出一直在執行或者卡住然後過一會才結束,這類的長時間執行的模糊測試,我還沒有摸透。如果有大佬知道的話麻煩也告訴我下。
參考
https://github.com/dvyukov/go-fuzz
https://go.dev/blog/fuzz-beta
到此這篇關於Golang之模糊測試工具的使用的文章就介紹到這了,更多相關Golang 模糊測試 內容請搜尋it145.com以前的文章或繼續瀏覽下面的相關文章希望大家以後多多支援it145.com!
相關文章
<em>Mac</em>Book项目 2009年学校开始实施<em>Mac</em>Book项目,所有师生配备一本<em>Mac</em>Book,并同步更新了校园无线网络。学校每周进行电脑技术更新,每月发送技术支持资料,极大改变了教学及学习方式。因此2011
2021-06-01 09:32:01
综合看Anker超能充系列的性价比很高,并且与不仅和iPhone12/苹果<em>Mac</em>Book很配,而且适合多设备充电需求的日常使用或差旅场景,不管是安卓还是Switch同样也能用得上它,希望这次分享能给准备购入充电器的小伙伴们有所
2021-06-01 09:31:42
除了L4WUDU与吴亦凡已经多次共事,成为了明面上的厂牌成员,吴亦凡还曾带领20XXCLUB全队参加2020年的一场音乐节,这也是20XXCLUB首次全员合照,王嗣尧Turbo、陈彦希Regi、<em>Mac</em> Ova Seas、林渝植等人全部出场。然而让
2021-06-01 09:31:34
目前应用IPFS的机构:1 谷歌<em>浏览器</em>支持IPFS分布式协议 2 万维网 (历史档案博物馆)数据库 3 火狐<em>浏览器</em>支持 IPFS分布式协议 4 EOS 等数字货币数据存储 5 美国国会图书馆,历史资料永久保存在 IPFS 6 加
2021-06-01 09:31:24
开拓者的车机是兼容苹果和<em>安卓</em>,虽然我不怎么用,但确实兼顾了我家人的很多需求:副驾的门板还配有解锁开关,有的时候老婆开车,下车的时候偶尔会忘记解锁,我在副驾驶可以自己开门:第二排设计很好,不仅配置了一个很大的
2021-06-01 09:30:48
不仅是<em>安卓</em>手机,苹果手机的降价力度也是前所未有了,iPhone12也“跳水价”了,发布价是6799元,如今已经跌至5308元,降价幅度超过1400元,最新定价确认了。iPhone12是苹果首款5G手机,同时也是全球首款5nm芯片的智能机,它
2021-06-01 09:30:45