Go Fuzz Testing
Fuzz test structure
Fuzz tests look like:
func Fuzz_MyTest(f *testing.F) {
f.Add([]byte{1, 2, 3})
f.Fuzz(func(t *testing.T, a []byte){
// Test logic applied to the input.
}
}
Fuzz_MyTest(f *testing.F)
declares the fuzz test.
f.Add(...)
specifies a “seed corpus”. This is used as a starting point for the fuzzer to generate randomized inputs. We can have more than one f.Add(...)
if we want to make sure specific cases are covered.
f.Fuzz(func(t *testing.T, ...)
speficies the actual test to run on the randomized inputs.
In this case I have specified a []byte
as the input argument. However, fuzzing arguments can be one or more of any of the following types:
int, int8, int16, int32, int64,
uint, uint8, uint16, uint32, uint64,
rune, byte, []byte, string,
float32, float64, bool
Running fuzz tests
We can call:
go test .
This will run our fuzz test with the “seed corpuses” (and generated corpuses if those exist), but no randomized tests will be run.
To run the fuzzer, run:
go test -fuzz=.
This will run until a failure occurs. If it is impossible to make the test fail or panic, this command will run forever.
If we want the fuzzer to run for a specific amount of time (i.e for CI), then run:
go test -fuzz=. -fuzztime=5s
This will run the fuzzer for 5 seconds only.
Generated corpus
When a fuzz test fails, it will generate something called a “generated corpus”, which is a specification of the exact inputs which caused the failure.
This will be stored in a testdata/fuzz/<test name>/<hash>
file which will look something like:
go test fuzz v1
[]byte("")
Where the second line specifies the inputs.
You may consider commiting the generated corpus to your project, because it provides regression for your test - that is, it will be run alongside your “seed corpus” whenever your fuzz test is run with go test
.