Appendix F. Fuzzing
Testing is central to software quality, and fuzz testing adds a powerful dimension by exploring code with smartly generated random inputs to uncover crashes, unexpected behaviors, and subtle edge cases. Integrated into Go since version 1.18, fuzzing complements—not replaces—unit tests, TDD, and benchmarks. It’s especially effective for surfacing security issues and for equivalence checking, where two implementations are validated to behave identically across a wide range of inputs, which is invaluable during refactoring.
In Go, a fuzz test lives in a _test.go file, uses a function named FuzzXxx that accepts *testing.F, seeds an initial input corpus with f.Add, and runs mutations via f.Fuzz. Only certain basic types are supported in the corpus. A worked example builds a numeric palindrome checker and a fuzz test that cross-validates results by comparing the function’s output with that of a reference approach that reverses the input. Running fuzzing with go test -fuzz executes inputs in parallel, reports coverage baselines, execution rates, “new interesting” inputs that exercise novel paths, and stops on the first failure while saving the offending input under testdata/fuzz for deterministic reproduction.
The palindrome case demonstrates how fuzzing quickly exposed a flaw with leading zeros (for example, "01"), prompting a redesign that avoids integer conversion, checks the input is digits-only, and compares runes from both ends to preserve semantics. Expanding the seed corpus with tricky cases (empty strings, leading/trailing zeros, signs, very large values, non-numeric strings) improves coverage, and options like -fuzztime bound runtime for CI. Recommended practices include crafting a meaningful seed corpus, asserting correctness and safety properties, and using fuzzing alongside unit and property tests—especially for parsers and input handlers. Common pitfalls are over-reliance on fuzzing, neglecting small domains where exhaustive testing is better, performance costs from long runs, and interacting with external systems without isolation. Even with robust fuzzing, the absence of failures is not proof of correctness, so continued vigilance is essential.
F.7 Summary
- Fuzz testing is a powerful tool for uncovering hidden bugs and improving the robustness of your code by automatically generating unexpected inputs. It complements traditional testing methods, helping you catch edge cases and prevent crashes.
- For more advanced fuzzing techniques, explore the official Go documentation and community resources. And once you’ve found some bugs, join the fun by adding them to Go's Fuzzing Trophy Case (https://go.dev/wiki/Fuzzing-trophy-case).
FAQ
What is fuzz testing and how does it complement unit tests?
Fuzz testing feeds smartly generated random inputs into your code to explore new code paths and uncover crashes, edge cases, and security issues you didn’t anticipate. It complements unit tests by finding inputs you didn’t think to write by hand; use both together for better coverage and resilience.How do I write a fuzz test in Go?
Place your test in a_test.go file, name the function FuzzXxx, and accept a *testing.F parameter. Seed initial inputs with f.Add(...), then define the fuzz target with f.Fuzz(func(t *testing.T, arg1 Type1, ...){ ... }). Inside the function, call the function under test and assert properties with t.Errorf or t.Fatal.Which input types does f.Add support?
You can seed the corpus with values of these types: []byte, string, rune, int, intXX, uint, uintXX, float32, and float64. The parameter types in your f.Fuzz callback must match the seeded types.What is a seed corpus and how should I choose inputs?
The seed corpus is the set of initial inputs you provide viaf.Add to guide the fuzzer toward interesting areas. Include nominal cases and edge cases (empty strings, very large or small values, malformed inputs, non-ASCII, etc.). A good corpus helps the fuzzer reach deeper, varied code paths faster.How do I run fuzz tests and control their duration?
Run fuzz tests withgo test -fuzz=Regex, where the regex matches your fuzz function name (e.g., go test -fuzz=IsPalindromeNumber). Fuzzing runs until it finds a failure or you stop it; use Ctrl+C (or Command-C on macOS) to stop, or set a time limit with -fuzztime=30s (for example).How do I interpret the fuzzing output (elapsed, execs, new interesting, workers)?
- Baseline coverage shows runs over your seed inputs before fuzzing begins.- “workers” is the number of parallel goroutines generating and executing inputs.
- “execs” is the total cases executed and the current throughput.
- “new interesting” counts inputs that exercise new code paths or behaviors, expanding the corpus.
What happens when a fuzz test fails, and how can I reproduce it?
Go prints the failing input and writes it totestdata/fuzz/<FuzzName>/<hash>. Reproduce the failure deterministically with go test -run=<FuzzName>/<hash>. Keeping these files (or adding equivalent seeds with f.Add) preserves regressions.How did fuzzing help improve the numeric palindrome example?
The fuzzer found that inputs like"01" tricked an integer-based approach (Atoi strips leading zeros). The fix compared runes from both ends and validated that the string contained only digits with a regex, correctly handling leading zeros and very long inputs.When should I use fuzzing versus exhaustive or traditional testing?
- Use fuzzing for parsers, format readers, and input-heavy code where the input space is large and surprising combinations matter.- For small finite spaces (e.g.,
uint16), exhaustive tests may be better.- Fuzzing complements, not replaces, unit tests, property tests, and benchmarks.
What are best practices and pitfalls for fuzzing in Go?
- Best practices: build a rich seed corpus, assert meaningful properties, compare implementations to check equivalence, and retain failing inputs.- Pitfalls: over-relying on fuzzing alone, ignoring non-nominal inputs (e.g., non-numeric strings), fuzzing code with external side effects without isolation, and neglecting performance costs of long-running or IO-heavy fuzz targets.
Learn Go with Pocket-Sized Projects ebook for free