What does `go test -fuzz=FuzzParseQuery -fuzztime=30s` do that a plain `go test` does not?
answer
- one flag turns a test into a search
- without it, only the seeds run
- it does not stop on its own
- a duration, or an Nx count
- exactly one target, one package
basics
~20 sIt switches the package into fuzzing mode: after the ordinary tests and the seeds, the engine generates mutated inputs for 30 seconds, keeping any that reach new coverage. Plain go test only replays the seeds and stops.
solid answer
~40 sWithout `-fuzz`, `go test` runs each fuzz target once per seed entry and finishes in milliseconds. With `-fuzz=FuzzParseQuery` it runs the package's normal tests and all seed corpora first, then begins the search: worker processes mutate inputs, coverage instrumentation decides which mutations were interesting enough to keep, and the run continues until a failure or until the budget is used. `-fuzztime` sets that budget — a duration like `30s`, or an iteration count in the `1000x` form. Without `-fuzztime` there is no limit at all: the run continues until it finds a failure or you interrupt it, which is right at a workstation and wrong in a pipeline. `-fuzz` also takes a regular expression that must match exactly one target, and it may only be used when testing a single package.
code
text · 3 linesgo test ./query # seeds only, like a unit test
go test ./query -fuzz=FuzzParseQuery -fuzztime=30s # mutate for 30 seconds
go test ./query -fuzz=FuzzParseQuery -fuzztime=1000x # mutate for 1000 iterationsgo deeper
Know the two modes by their command lines: no flag means the target runs on its seeds like a test, and -fuzz starts the search that invents new inputs.
Explain the coverage-guided loop, what -fuzztime bounds and what its two forms are, and why -fuzz is limited to one target in one package.
Talk about running it for real: bounded budgets in a scheduled lane, seeds-only on every change, the instrumentation and worker-process cost, and how a found failure gets triaged.
Own where the compute goes — which targets earn a recurring budget, how long, on whose machines, and what happens to the finding when the scheduled job goes red.
## Two modes, one target The same `FuzzParseQuery` function is used in two very different ways. **Seeds-only mode** is what you get from `go test ./query` with no `-fuzz` flag. Every fuzz target in the package is collected like a test and its callback is invoked once for each seed corpus entry. Nothing is generated. It is fast, deterministic, and safe to run on every change: it turns every input that has ever broken the parser into a permanent regression test. **Fuzzing mode** is what `-fuzz` turns on. It is a coverage-guided search, not a test run. ## What -fuzz actually does `-fuzz` takes a regular expression matched against fuzz target names. Before the search starts, `go test` runs the package's ordinary unit tests and every target's seed corpus — a failure there stops everything, because fuzzing a package whose tests are already red tells you nothing. Then the coordinator process builds a baseline coverage picture from the seeds and starts worker processes, by default as many as `-parallel` allows. Each worker takes an existing input, mutates it — flipping bytes, splicing pieces of two entries together, nudging an integer to a boundary — and runs the callback. If the mutated input executed code no earlier input reached, it is kept and becomes a base for further mutation. That feedback loop is why fuzzing finds deep parser bugs that random generation never would: it climbs into the grammar one branch at a time. When the callback panics or fails an assertion, the run stops, the failing input is printed, and it is written out so the failure can be reproduced later. ## Two constraints on -fuzz - **The regexp must match exactly one target.** Fuzzing is a single coordinated search with its own workers and its own coverage picture; matching two targets would split the budget between them, so `go test` reports an error instead. - **Only one package may be under test.** `go test -fuzz=... ./...` is rejected. Point it at one package. Both constraints follow from the same fact: fuzzing is not a batch of tests, it is one long-running process with state. ## -fuzztime `-fuzztime` bounds the *fuzzing* phase only; the tests and seed run before it are not counted against the budget. It accepts either: - **a duration**, e.g. `-fuzztime=30s`, `-fuzztime=10m`, parsed the way `time.Duration` strings are; or - **an iteration count**, in the `Nx` form: `-fuzztime=1000x` runs the callback 1000 times. The default is no limit. That default is deliberate — fuzzing is an open-ended search and there is no natural point at which it is "done" — but it means a naive pipeline step that passes `-fuzz` and nothing else will hang until the job times out. A bounded budget is what makes fuzzing schedulable. ``` go test ./query # seeds only go test ./query -fuzz=FuzzParseQuery -fuzztime=30s # 30 seconds of search go test ./query -fuzz=FuzzParseQuery -fuzztime=500x # 500 iterations ``` ## How teams usually wire it up The common shape is two lanes. Every change runs the whole suite with no `-fuzz` flag, so every target is exercised over its seeds in the normal test time. Separately, a scheduled job runs `-fuzz` against one target at a time with a real budget — minutes, not seconds — on a machine that can afford it. A non-zero exit from the scheduled job means the engine found something, and the input it saved is what the developer picks up. A useful companion flag is `-run` in the seeds-only lane: `go test -run=FuzzParseQuery ./query` restricts the run to that target's seeds when you are iterating on it. ## Costs to expect Fuzzing builds the package with coverage instrumentation, so the binary is slower than a normal test build; it runs several worker processes; and the engine keeps a growing set of interesting inputs on disk between runs. None of this is free, which is another reason the search belongs in a scheduled lane rather than in the fast feedback loop.
- What is the default -fuzztime, and why does that matter for an automated pipeline?There is no default limit: the run continues until it finds a failure or is interrupted. That is right on a workstation and wrong in a pipeline, so an automated lane either omits `-fuzz` entirely — leaving targets as fast seed-only regression tests — or gives an explicit budget such as `-fuzztime=60s` in a scheduled job, and treats a non-zero exit as a real defect to triage.
- Why does go test refuse a -fuzz regexp that matches two targets?Fuzzing is one coordinated search: a coordinator, worker processes, and a coverage picture that belongs to a single target. Running two at once would split the budget and blur the feedback that decides which mutations are worth keeping. Run them in separate invocations, or rotate targets across scheduled runs.
- What does -fuzztime=1000x mean, and when would you prefer it to a duration?It requests 1000 iterations of the fuzz callback instead of a wall-clock budget. It is useful when the machines in a fleet differ wildly in speed and you want the amount of work to be comparable across them, or when you want a smoke-sized run that is reproducible in effort. On a fast machine a duration usually buys more search per unit of pipeline time.
saying these in an interview costs you the question
- Thinks plain go test fuzzes without the -fuzz flag
- Expects -fuzz to stop after one pass over the corpus
- Runs -fuzz across ./... with no time budget in automation
- Believes -fuzztime accepts only a duration
- Assumes -fuzz can fuzz several targets at once