What Is Fuzz Testing?
Fuzz testing throws large amounts of random, malformed, or unexpected input at a program to find crashes, hangs, and security flaws.
Fuzz testing, or fuzzing, is the practice of bombarding code with garbage input to see what breaks. Where property-based testing checks logical properties, fuzzing mostly hunts for the input that makes a program crash, hang, or misbehave. It is a staple of security testing for parsers, decoders, and anything that handles untrusted data.
How fuzzing works
A fuzzer feeds the target a stream of generated inputs and watches for bad outcomes: crashes, memory errors, infinite loops, or assertion failures. Each interesting failure is saved as a reproducible case. The more input it tries, the deeper into edge cases it reaches.
Coverage-guided fuzzing
Modern fuzzers are coverage-guided: they instrument the code and favor inputs that reach new code paths, evolving toward inputs that exercise more of the program. This makes them far more effective than blind random input at finding deep bugs.
A quick example
A fuzz target hands arbitrary bytes to a parser; the harness fails if the parser ever crashes on input it should reject gracefully.
func FuzzParse(f *testing.F) {
f.Fuzz(func(t *testing.T, data []byte) {
Parse(data) // must not panic
})
}Where fuzzing pays off
- Parsers, decoders, and serializers.
- Anything that processes untrusted external input.
- Security-sensitive code paths.
- Finding crashes that examples never trigger.
Fuzzing in CI
Fuzzing is open-ended, so teams usually run a short fuzz pass on each pull request and longer continuous fuzzing on a schedule. Both benefit from parallel runs on fast runners, since more compute means more inputs tried and more bugs found per hour.
Key takeaways
- Fuzz testing feeds random or malformed input to find crashes and flaws.
- Coverage-guided fuzzers evolve inputs toward new code paths.
- Run short fuzzing per PR and longer scheduled fuzzing in CI.