自拍偷在线精品自拍偷,亚洲欧美中文日韩v在线观看不卡

關于 Golang 的模糊測試實踐

開發(fā)
模糊測試可以簡單快速的自動化構建測試用例,盡量遍歷各種可能的輸入場景,從而保證函數代碼覆蓋盡可能多的邊緣場景。Go原生內置了模糊測試的支持,如果善加利用,可以有效提升Go代碼的質量。

導言

在 Go 編程領域,有一個提升代碼安全性的秘密武器:模糊測試(fuzz testing)。想象一下,有個機器人不知疲倦的向你的 Go 程序扔出除了廚房水槽以外的所有東西,以確保它們堅如磐石。模糊測試不是常規(guī)、可預測的測試,而是測試意料之外的、離奇的場景,用隨機數據挑戰(zhàn)代碼,以發(fā)現隱藏 bug。

Go 的出現讓模糊測試變得輕而易舉。由于工具鏈內置了支持,Go 開發(fā)人員可以輕松的將這種強大的測試方法自動化。這就像為代碼配備了時刻保持警惕的守護者,不斷查找那些可能會漏掉的偷偷摸摸的 bug。

Go 模糊測試就是要將代碼推向極限,甚至超越極限,以確保代碼在現實世界中能夠抵御任何奇特而美妙的輸入。這證明了 Go 對可靠性和安全性的承諾,在一個軟件需要堅如磐石的世界里,它能讓人高枕無憂。

因此,如果你發(fā)現應用程序即使在最意想不到的情況下也能流暢運行時,請記住模糊測試所發(fā)揮的作用,它作為無名英雄在幕后為 Go 應用的順利運行而努力。

種子語料庫(Seed Corpus):高效模糊測試的基礎

種子語料庫是提供給模糊測試流程的初始輸入集合,用于啟動生成測試用例,可以把它想象成鎖匠用來制作萬能鑰匙的初始鑰匙集。在模糊測試中,這些種子作為起點,模糊器從中衍生出多種變體,探索大量可能的輸入以發(fā)現錯誤。通過精心挑選一組具有代表性的多樣化種子,可以確保模糊器從一開始就能覆蓋更多領域,從而使測試過程更高效且有效。種子可以是典型用例數據,也可以是邊緣用例或以前發(fā)現的可誘發(fā)錯誤的輸入,從而為徹底測試軟件的可靠性奠定基礎。

示例:對 Go 字符串反轉函數進行模糊測試

我們用 Go 編寫一個簡單的字符串反轉函數,然后創(chuàng)建一個模糊測試。這個示例將有助于說明模糊測試如何在看似簡單的函數中發(fā)現意想不到的行為或錯誤。

Go 函數:反轉字符串

package main

// ReverseString takes a string as input and returns its reverse.
func ReverseString(s string) string {
    // Convert the string to a rune slice to properly handle multi-byte characters.
    runes := []rune(s)
    for i, j := 0, len(runes)-1; i < j; i, j = i+1, j-1 {
        // Swap the runes.
        runes[i], runes[j] = runes[j], runes[i]
    }
    // Convert the rune slice back to a string and return it.
    return string(runes)
}

解釋:

  • ReverseString 函數:該函數接收一個字符串參數并返回其反轉值。它將字符串作為 rune 切片而不是字節(jié)來處理,這對于正確處理大小可能超過一個字節(jié)的 Unicode 字符至關重要。
  • Rune 切片:通過將字符串轉換為 rune 切片,可以確保正確處理多字節(jié)字符,保持字符編碼的完整性。
  • 交換:該函數迭代 rune 切片,從兩端開始交換元素,然后向中心移動,從而有效反轉切片。

ReverseString 函數的模糊測試

我們?yōu)檫@個函數編寫模糊測試:

package main

import (
    "testing"
    "unicode/utf8"
)

// FuzzReverseString tests the ReverseString function with fuzzing.
func FuzzReverseString(f *testing.F) {
    // Seed corpus with examples, including a case with Unicode characters.
    f.Add("hello")
    f.Add("world")
    f.Add("こんにちは") // "Hello" in Japanese

    f.Fuzz(func(t *testing.T, original string) {
        // Reverse the string twice should give us the original string back.
        reversed := ReverseString(original)
        doubleReversed := ReverseString(reversed)
        if original != doubleReversed {
            t.Errorf("Double reversing '%s' did not give original string, got '%s'", original, doubleReversed)
        }

        // The length of the original and the reversed string should be the same.
        if utf8.RuneCountInString(original) != utf8.RuneCountInString(reversed) {
            t.Errorf("The length of the original and reversed string does not match for '%s'", original)
        }
    })
}

解釋:

  • 種子語料庫:我們從一組種子輸入開始,包括簡單的 ASCII 字符串和一個 Unicode 字符串,以確保模糊測試涵蓋一系列字符編碼。
  • 模糊函數:模糊函數反轉字符串,然后再反轉回來,期望得到原始字符串。這是一個簡單的不變量,如果反轉函數正確的話,就應該總是成立的。它還會檢查原始字符串和反轉字符串的長度是否相同,以檢查多字節(jié)字符可能出現的問題。
  • 運行測試:要運行該模糊測試,請使用帶有 -fuzz 標志的 go test 命令,如:go test -fuzz=Fuzz。

構建用于數據持久化的 Go REST API 的模糊測試

要在 Go 中創(chuàng)建一個接受 POST 請求并將接收到的數據存儲到文件中的 REST API,可以使用 net/http 軟件包。我們將為處理 POST 請求數據的函數編寫模糊測試。請注意,由于模糊測試的性質及其適用性,此處的模糊測試將重點測試數據處理邏輯,而非 HTTP 服務器本身。

步驟 1:處理 POST 請求的 REST API 函數

首先需要設置一個簡單的 HTTP 服務器,保證其路由可以處理 POST 請求。該服務器將把 POST 請求正文保存到文件中。

package main

import (
 "io/ioutil"
 "log"
 "net/http"
)

func main() {
 http.HandleFunc("/save", saveDataHandler) // Set up the route
 log.Println("Server starting on port 8080...")
 log.Fatal(http.ListenAndServe(":8080", nil))
}

// saveDataHandler saves the POST request body into a file.
func saveDataHandler(w http.ResponseWriter, r *http.Request) {
 if r.Method != http.MethodPost {
  http.Error(w, "Only POST method is allowed", http.StatusMethodNotAllowed)
  return
 }

 // Read the body of the POST request
 body, err := ioutil.ReadAll(r.Body)
 if err != nil {
  http.Error(w, "Error reading request body", http.StatusInternalServerError)
  return
 }
 defer r.Body.Close()

 // Save the data into a file
 err = ioutil.WriteFile("data.txt", body, 0644)
 if err != nil {
  http.Error(w, "Error saving file", http.StatusInternalServerError)
  return
 }

 w.WriteHeader(http.StatusOK)
 w.Write([]byte("Data saved successfully"))
}

這個簡單的服務監(jiān)聽 8080 端口,并有一個接受 POST 請求的路由 /save。該路由的處理程序 saveDataHandler 會讀取請求正文并將其寫入名為 data.txt 的文件中。

步驟 2:編寫模糊測試

在模糊測試中,我們將重點關注將數據保存到文件中的功能。由于無法直接對 HTTP 服務器進行模糊測試,我們把處理數據的邏輯提取到單獨的函數中,并對其進行模糊測試。

package main

import (
 "bytes"
 "net/http"
 "net/http/httptest"
 "testing"
)

// FuzzSaveDataHandler uses f.Fuzz to fuzz the body of POST requests sent to saveDataHandler.
func FuzzSaveDataHandler(f *testing.F) {
 // Seed corpus with examples, including different types and lengths of data.
 f.Add([]byte("example data")) // Example seed
 f.Add([]byte(""))             // Empty seed

 f.Fuzz(func(t *testing.T, data []byte) {
  // Construct a new HTTP POST request with fuzzed data as the body.
  req, err := http.NewRequest(http.MethodPost, "/save", bytes.NewReader(data))
  if err != nil {
   t.Fatalf("Failed to create request: %v", err)
  }

  // Create a ResponseRecorder to act as the target of the HTTP request.
  rr := httptest.NewRecorder()

  // Invoke the saveDataHandler with our request and recorder.
  saveDataHandler(rr, req)

  // Here, you can add assertions based on the expected behavior of your handler.
  // For example, checking that the response status code is http.StatusOK.
  if rr.Code != http.StatusOK {
   t.Errorf("Expected status OK for input %v, got %v", data, rr.Code)
  }

  // Additional assertions can be added here, such as verifying the response body
  // or the content of the "data.txt" file if necessary.
 })
}

解釋:

  • FuzzSaveDataHandler 函數:該函數測試 saveDataHandler 如何處理不同 POST 請求體,基于模糊測試來嘗試各種輸入數據。
  • 種子語料庫:測試從一些示例數據("example data"和空字符串)開始,以指導模糊處理過程。

執(zhí)行模糊測試:

  • 對于每個模糊輸入,都會向處理程序發(fā)出 POST 請求。
  • ResponseRecorder會捕捉處理程序對這些請求的響應。
  • 測試將檢查處理程序是否對所有輸入都響應 http.StatusOK 狀態(tài),從而判斷是否已成功處理這些輸入。

運行測試使用 go test -fuzz=FuzzSaveDataHandler 運行模糊測試。測試從種子數據中生成各種輸入,并檢查處理程序響應。

在 Go 中驗證和存儲 CSV 數據:模糊測試方法

要創(chuàng)建一個讀取 CSV 文件、驗證其值并將驗證后的數據存儲到文件中的函數,我們將按照以下步驟進行操作:

  • 處理 CSV 的函數:該函數將讀取 CSV 數據,根據預定義規(guī)則驗證其內容(為簡單起見,假設我們期望兩列具有特定的數據類型),然后將驗證后的數據存儲到新文件中。
  • 模糊測試:我們將為驗證 CSV 數據的函數部分編寫模糊測試。這是因為模糊測試非常適合測試代碼如何處理各種輸入,而我們將重點關注驗證邏輯。

步驟 1:處理和驗證 CSV 數據的功能

package main

import (
 "encoding/csv"
 "fmt"
 "io"
 "os"
 "strconv"
)

// validateAndSaveData reads CSV data from an io.Reader, validates it, and saves valid rows to a file.
func validateAndSaveData(r io.Reader, outputFile string) error {
 csvReader := csv.NewReader(r)
 validData := [][]string{}

 for {
  record, err := csvReader.Read()
  if err == io.EOF {
   break
  }
  if err != nil {
   return fmt.Errorf("error reading CSV data: %w", err)
  }

  if validateRecord(record) {
   validData = append(validData, record)
  }
 }

 return saveValidData(validData, outputFile)
}

// validateRecord checks if a CSV record is valid. For simplicity, let's assume the first column should be an integer and the second a non-empty string.
func validateRecord(record []string) bool {
 if len(record) != 2 {
  return false
 }

 if _, err := strconv.Atoi(record[0]); err != nil {
  return false
 }

 if record[1] == "" {
  return false
 }

 return true
}

// saveValidData writes the validated data to a file.
func saveValidData(data [][]string, outputFile string) error {
 file, err := os.Create(outputFile)
 if err != nil {
  return fmt.Errorf("error creating output file: %w", err)
 }
 defer file.Close()

 csvWriter := csv.NewWriter(file)
 for _, record := range data {
  if err := csvWriter.Write(record); err != nil {
   return fmt.Errorf("error writing record to file: %w", err)
  }
 }
 csvWriter.Flush()
 return csvWriter.Error()
}

步驟 2:驗證邏輯的模糊測試

在模糊測試中,我們將重點關注 validateRecord 函數,該函數負責驗證 CSV 數據的各個行。

package main

import (
 "strings"
 "testing"
)

// FuzzValidateRecord tests the validateRecord function with fuzzing.
func FuzzValidateRecord(f *testing.F) {
 // Seed corpus with examples, joined as single strings
 f.Add("123,validString")   // valid record
 f.Add("invalidInt,string") // invalid integer
 f.Add("123,")              // invalid string

 f.Fuzz(func(t *testing.T, recordStr string) {
  // Split the string back into a slice
  record := strings.Split(recordStr, ",")

  // Now you can call validateRecord with the slice
  _ = validateRecord(record)
  // Here you can add checks to verify the behavior of validateRecord
 })
}

運行模糊測試:

要運行這個模糊測試,需要使用帶有 -fuzz 標志的 go test 命令:

go test -fuzz=Fuzz

該命令將啟動模糊處理過程,根據提供的種子自動生成和測試各種輸入。

說明:

  • validateAndSaveData 函數從io.Reader讀取數據,從而可以處理來自任何實現此接口的數據源(如文件或內存緩沖區(qū))的數據。該函數通過 csv.Reader 解析 CSV 數據,使用 validateRecord 驗證每條記錄,并存儲有效記錄。
  • validateRecord 函數旨在根據簡單的規(guī)則驗證每條 CSV 記錄:第一列必須可轉換為整數,第二列必須是非空字符串。
  • saveValidData 函數獲取經過驗證的數據,并以 CSV 格式將其寫入指定的輸出文件。
  • validateRecord 的模糊測試使用種子輸入來啟動模糊處理過程,用大量生成的輸入值來測試驗證邏輯,以發(fā)現潛在的邊緣情況或意外行為。

測試似乎一直在進行

fuzz: elapsed: 45s, execs: 7257 (0/sec), new interesting: 0 (total: 2)
fuzz: elapsed: 48s, execs: 7257 (0/sec), new interesting: 0 (total: 2)
fuzz: elapsed: 51s, execs: 7257 (0/sec), new interesting: 0 (total: 2)
fuzz: elapsed: 54s, execs: 7257 (0/sec), new interesting: 0 (total: 2)
fuzz: elapsed: 57s, execs: 7257 (0/sec), new interesting: 0 (total: 2)
fuzz: elapsed: 1m0s, execs: 7257 (0/sec), new interesting: 0 (total: 2)
fuzz: elapsed: 1m3s, execs: 7848 (197/sec), new interesting: 4 (total: 6)
fuzz: elapsed: 1m6s, execs: 9301 (484/sec), new interesting: 4 (total: 6)
fuzz: elapsed: 1m9s, execs: 11457 (718/sec), new interesting: 4 (total: 6)
fuzz: elapsed: 1m12s, execs: 14485 (1009/sec), new interesting: 4 (total: 6)
fuzz: elapsed: 1m15s, execs: 16927 (814/sec), new interesting: 4 (total: 6)

當模糊測試似乎無限期或長時間運行時,通常意味著它在不斷生成和測試新的輸入。模糊測試是一個密集的過程,會消耗大量時間和資源,尤其是當被測功能涉及復雜操作或模糊器發(fā)現許多"有趣"的輸入,從而探索出新的代碼路徑時。

以下是可以采取的幾個步驟,用于管理和減少長時間運行的模糊測試:

1.限制模糊測試時間

可以在運行模糊測試時使用 -fuzztime 標志來限制模糊測試的持續(xù)時間。例如,要使模糊測試最多運行 1 分鐘,可以使用:

go test -fuzz=FuzzSaveDataHandler -fuzztime=1m

2.審查和優(yōu)化測試代碼

如果代碼的某些部分特別慢或消耗資源,請考慮盡可能對其進行優(yōu)化。由于模糊測試會產生大量請求,即使代碼效率稍微低一點,也會被放大。

3.調整種子語料庫

檢查提供給模糊器的種子語料庫,確保其多樣性足以探索各種代碼路徑,但又不會過于寬泛,導致模糊器陷入過多路徑。有時,過于通用的種子會導致模糊器在無益路徑上花費過多時間。

4.監(jiān)控"有趣的"輸入

模糊器會報告覆蓋新代碼路徑或觸發(fā)獨特行為的"有趣"輸入。如果"有趣"輸入的數量大幅增加,則可能表明模糊器正在不斷發(fā)現新的探索場景。查看這些輸入可以深入了解代碼中的潛在邊緣情況或意外行為。

5.分析模糊器性能

輸出顯示了每秒執(zhí)行次數,可以讓我們了解模糊器的運行效率。如果執(zhí)行率很低,可能說明模糊器設置或被測代碼存在性能瓶頸。調查并解決這些瓶頸有助于提高模糊器的效率。

6.考慮手動中斷

如果模糊測試運行時間過長而沒有提供額外價值(例如,沒有發(fā)現新的有趣案例,或者已經從當前運行中獲得了足夠信息),可以手動停止該進程,然后查看迄今為止獲得的結果,以決定下一步行動(例如調整模糊參數或調查已發(fā)現的案例)。

責任編輯:趙寧寧 來源: DeepNoMind
相關推薦

2017-01-11 22:41:05

2023-08-01 09:27:44

Golang模糊測試

2020-05-19 08:52:31

APP滲透測試終端安全

2009-09-14 18:06:18

LINQ模糊查詢

2024-03-08 22:39:55

GolangApacheKafka

2025-01-13 06:00:00

Go語言gRPC

2024-04-28 18:24:05

2019-08-12 15:17:23

USB模糊測試

2017-05-08 07:37:56

2010-09-17 15:12:28

2023-03-30 07:52:03

Golang接口

2023-07-28 09:48:37

2019-07-20 23:30:48

開發(fā)技能代碼

2023-07-25 11:22:31

2012-02-22 14:18:06

測試測試人員

2023-12-25 09:49:01

Golang架構Go-Kit

2017-03-30 22:16:21

DevOpsIT應用程序

2024-01-07 12:47:35

Golang流水線設計模式

2024-05-28 00:00:30

Golang數據庫

2021-06-25 14:50:21

DevSecOps安全 DevOps
點贊
收藏

51CTO技術棧公眾號