導入:なぜファズテストに時間制限が必要なのか
Go 1.18から導入された「ファズテスト(Fuzzing)」は、ランダムな入力を自動生成してプログラムのバグやクラッシュを検出する非常に強力な手法です。しかし、ファズテストは基本的に「停止」の指示があるまで無限に実行され続ける性質を持っています。開発者のローカル環境なら手動で停止すれば良いですが、CI(継続的インテグレーション)環境ではそうはいきません。ビルドパイプラインを長時間占有したり、タイムアウトで強制終了されたりすることを防ぐために、実行時間を制御する「-fuzztime」フラグは実務において必須の知識です。
基礎知識:ファズテストと-fuzztimeの仕組み
Goのファズテストは、go testコマンドに-fuzzフラグを付けることで開始されます。デフォルトでは、何らかの障害が見つかるか、人間がCtrl+Cを押すまで走り続けます。
ここで登場するのが-fuzztimeです。このオプションを使用することで、テストの実行時間を「秒単位」や「分単位」で指定できます。これにより、CIパイプラインの制限時間内に収まるようにファズテストを調整し、効率的な品質保証が可能になります。
実装:-fuzztimeを活用したテスト実行
具体的な使い方は非常にシンプルです。ターミナルでgo testを実行する際、-fuzztimeフラグに「30s(30秒)」「1m(1分)」などの値を渡すだけです。
サンプルプログラム:安全なテスト実行コード
以下のコードは、入力された文字列を反転させて元に戻す処理が正しく動作するかをテストする例です。
// fuzz_test.go
package main
import (
“testing”
)
// FuzzReverse は文字列を反転させて元に戻るかを確認するファズテスト
func FuzzReverse(f testing.F) {
// 初期入力値(シード)を追加
f.Add(“hello”)
f.Add(“Go言語”)
f.Fuzz(func(t testing.T, orig string) {
// ここでテストしたいロジックを実行する
// 例: 文字列反転処理が正しく動作するかを検証
reversed := reverse(orig)
doubleReversed := reverse(reversed)
if orig != doubleReversed {
t.Errorf(“元の値: %s, 戻り値: %s”, orig, doubleReversed)
}
})
}
// ダミーの反転関数
func reverse(s string) string {
r := []rune(s)
for i, j := 0, len(r)-1; i < j; i, j = i+1, j-1 {
r[i], r[j] = r[j], r[i]
}
return string(r)
}
// 実行コマンド:
// CI環境で30秒間だけファズテストを実行する場合
// go test -fuzz=Fuzz -fuzztime=30s ./...
応用・注意点:現場での運用ノウハウ
1. CIでの使い分け
ローカル開発環境では-fuzztimeを指定せずにじっくりバグを探し、CIパイプラインでは-fuzztime=1mのように短く制限を設ける構成が推奨されます。
2. テストの非決定性に注意
ファズテストは実行するたびに異なる入力値で検証が行われます。CIで失敗した際は、生成された「テストケース(testdataディレクトリに保存されます)」を元に、何が原因で失敗したのかを特定してください。
3. 適切なタイムアウト設定
あまりに短い時間(数秒など)を設定すると、ファズテストが十分に探索できず、バグを見落とす可能性があります。プロジェクトの規模や計算量に応じて、CIの許容範囲内で可能な限り長い時間を設定するのがコツです。

コメント