🧪

【Go】Goのテストに入門してみた! ~Fuzzingテスト編~

に公開

はじめに

前回は「テーブル駆動ベンチマークテスト」を見ていきました。
今回は「Fuzzingテスト」に入門します。

前回の記事はこちら!
https://zenn.dev/tmyhrn/articles/28815a1296e856

この記事でわかること

  • Fuzzingテストの概要や役割
  • GoでのFuzzingテストの実施と挙動
  • GoでのFuzzingテストの実施ルール

Fuzzingテストとは?

Fuzzingテストとは、意図的に不正なデータや予期しないデータを入れて、システムの脆弱性を検出するテスト手法を指します。
さまざまなテストケースを考慮していると思っていても、予期しないデータの入力に関するテストが漏れていることもあるので、それをカバーすることができます。
Goではtestingパッケージに標準搭載されているので、簡単にfuzzingテストを実行することができます。

Fuzzingテストをやってみよう!

main_test.go
package main

import (
    "testing"
    "unicode/utf8"
)

func Length(s string) int {
    return utf8.RuneCountInString(s)
}

func FuzzLength(f *testing.F) {
    seeds := []string{"Hello", "こんにちは", "コンニチハ", "😃😃😃", ""}
    
    for _, seed := range seeds {
        f.Add(seed)
    }
    
    f.Fuzz(func(t *testing.T, input string) {
        got := Length(input)
        if got < 0 {
            t.Errorf("Length returned negative value: %d", got)
        }
        want := utf8.RuneCountInString(input)
        if got != want {
            t.Errorf("Length(%q) = %d, want %d", input, got, want)
        }
    })
}

以下のように実行することで、Fuzzingテストを実行することができます。

ターミナル
# go test -fuzz=FuzzLength -fuzztime=5s 

fuzz: elapsed: 0s, gathering baseline coverage: 0/25 completed
fuzz: elapsed: 0s, gathering baseline coverage: 25/25 completed, now fuzzing with 8 workers
fuzz: elapsed: 3s, execs: 768534 (256090/sec), new interesting: 0 (total: 25)
fuzz: elapsed: 5s, execs: 1288479 (248650/sec), new interesting: 0 (total: 25)
PASS
ok      go-test-practice        5.310s

今回は5秒間実施し、1288479件の入力をしたことが書かれています。
最終的にFuzzingテストが成功したことがここからわかります。

GoでのFuzzingテスト実施方法

GoでFuzzingテストを実施するには、いくつかルールがあります。
以下、ルールと実施方法の紹介です。

1. 関数名をFuzzからはじめる

必ず大文字のFからはじめるようにします。
引数には (t *testing.F) を渡すようにします。
返り値はありません。

2. f.Add()でシードコーパスを追加

f.Add() を使ってシードコーパス(初期入力値)を登録します。
Fuzzingテストではこれらの入力からスタートします。

3. f.Fuzz()でテスト実施

f.Fuzz(func(t *testing.T, <引数...>) {...}) という形にします。
引数にはシードコーパスの型を指定するようにしてください。
なお、Go Fuzzingによると、引数には以下の型のみ指定することができます。

The fuzzing arguments can only be the following types:

  • string, []byte
  • int, int8, int16, int32/rune, int64
  • uint, uint8/byte, uint16, uint32, uint64
  • float32, float64
  • bool

4. 実行方法

Fuzzingテストを実行するには、以下のフラグを用いる必要があります。

  • fuzz:実行するFuzz関数を指定
  • fuzztime:実行時間を指定
    ※デフォルトでは無制限なのでエラーが発生するまで実行し続ける

まとめ

今回は「Fuzzingテスト」について学びました。

Fuzzingテストには、以下の特徴があることがわかりました。

  • 通常のユニットテストでは漏れやすい「予期しない入力」による不具合を検出できる
  • Goの標準パッケージに組み込まれており、追加ライブラリなしで利用可能
  • シードコーパスを起点に、多様なテストケースを自動生成して検証可能

次回は「httptest」に入門します!

参考

https://go.dev/doc/security/fuzz/
https://gihyo.jp/book/2023/978-4-297-13419-8
https://www.blackduck.com/ja-jp/glossary/what-is-fuzz-testing.html
https://future-architect.github.io/articles/20220214a/
https://zenn.dev/10inoino/articles/fc2551b3b3355e
https://qiita.com/tsukasaI/items/3093ed27100469153421

Discussion