skip to content

Result Reuse and -count

go test prints (cached) and skips a package whose inputs are unchanged, which is how a broken test can look green for weeks. Knowing what invalidates that cache is the point of the question.

part ofGo (Golang)overview, primer and where to startread it →
on this pageshow

questions

4

In `go test` output, what does the `(cached)` marker mean, and how do you force a real run?

level: juniorimportance: must knowfreq 58%

answer

  1. the test binary never actually ran
  2. the marker sits where the duration goes
  3. failing runs are never reused
  4. one flag outside the cacheable set kills reuse

basics

~10 s

The (cached) marker means go test found a stored successful result for that package and reprinted its output instead of running the test binary. Only passing runs are stored; add -count=1 to force execution.

solid answer

~40 s

`ok example.com/pkg (cached)` means the go command recognised this exact package test run as one it has already done successfully, so it reprinted the earlier output instead of executing the test binary. The marker takes the place of the elapsed time, which is the giveaway that nothing ran. Only successful package results are cached — a failing run is never reused, so red output is always fresh. Caching happens in package list mode, for example `go test ./...` or `go test .`. To defeat it for one command, add `-count=1`: `-count` is not one of the flags the go command is willing to cache across, so the run is neither matched against the cache nor stored. To throw away every stored result on the machine instead, run `go clean -testcache`.

code

text · 8 lines
text
$ go test ./internal/rates/
ok  	example.com/billing/internal/rates	0.412s

$ go test ./internal/rates/
ok  	example.com/billing/internal/rates	(cached)

$ go test -count=1 ./internal/rates/
ok  	example.com/billing/internal/rates	0.409s

go deeper

for a junior

Be able to say in one sentence that the test binary did not run and that -count=1 makes it run. Recognise that the marker replaces the timing column in the summary line.

for a middle

Explain the mechanism behind the escape hatch: reuse requires every flag to come from a small cacheable set, and -count is not in it. Know that only passing package results are stored.

for a senior

Show that you treat a green (cached) line as a claim about observed inputs, not about the world. Say when you would clear results with go clean -testcache and when you would pin a job to -count=1 instead.

for a principal

Frame it as a trust boundary for the team: which suites are allowed to be cacheable, and which must always execute because they depend on state the go command cannot see. That policy is what keeps a repo-wide green meaningful.

## What `(cached)` actually reports When `go test` finishes a package successfully, the go command writes that package's test output and exit status into the build cache directory (`go env GOCACHE`) under a key derived from everything it believes the run depended on. The next time you ask for the same package with the same inputs, it does not compile-and-run anything: it looks the entry up, prints the stored output, and marks the summary line `(cached)` where the elapsed time normally goes. ``` ok example.com/billing/internal/rates 0.412s <- ran ok example.com/billing/internal/rates (cached) <- reprinted ``` So the marker is not "the tests ran quickly" and not "the tests were skipped". It is "the test binary was not executed; you are looking at a recording". With `-v`, the recorded verbose log is replayed too, which is why a cached run can print a wall of `--- PASS` lines instantly. ## Only successful runs are cached This is the property that makes the feature tolerable. A package that fails is re-run every single time, so you can never be looking at a stale failure. The risk runs entirely in the other direction: a stale *pass*, if something the go command could not observe changed underneath you. ## When caching applies at all The go command caches in package list mode — when you name packages, as in `go test ./...`, `go test .`, or `go test example.com/billing/...`. The local-directory form, plain `go test` with no package arguments, is documented as not caching, which is why some developers say they "never see (cached)" while a colleague sees it constantly. Results are stored per package: in a repo-wide run you routinely get a mix, a few packages executing and the rest replaying. ## Forcing a real run The conventional escape hatch is `-count=1`: ``` go test -count=1 ./internal/rates/ ``` It is worth knowing *why* this works, because the usual guess is wrong. `-count=1` is already the default number of runs, so it changes nothing about execution. It works because the go command only reuses a result when every flag on the command line comes from a small set it considers safe to cache across (`-run`, `-v`, `-short`, `-timeout`, `-cpu`, `-parallel`, `-failfast`, `-list`, `-fullpath`). `-count` is deliberately not in that set, so passing it makes the invocation uncacheable: the result is not matched against the cache and not stored either. Any other non-cacheable flag has the same effect; `-count=1` is simply the idiom because it is otherwise a no-op. The heavier hammer is `go clean -testcache`, which expires every stored test result in the build cache — all packages, all modules, everything that shares that `GOCACHE`. It leaves the compiled-package build cache alone, so your next build is still fast. `go clean -cache` is the one that wipes compiled artefacts too and makes the next build slow; reach for it only when you actually suspect the build cache. ## Why a package you did not touch can still be `(cached)` The entry is keyed on the test binary for *that* package, which is built from the package's own source plus all of its dependencies. Editing an unrelated package leaves that binary byte-identical, so the reuse is correct. Editing a package that the test imports rebuilds the binary and produces a different key, so it re-runs. That is the behaviour you want: the cache is a per-package memo, not a global timestamp. ## What to say in an interview Name the three facts: the binary did not run, only passes are cached, and `-count=1` disables reuse because `-count` is outside the cacheable flag set. Then add the honest caveat — the cache only knows about inputs the go command can see, so a test that talks to a service or reads a file outside its own source tree can go on printing `(cached)` after the thing it depends on has changed.

  • Can a failing test result ever come back as (cached)?
    No. The go command stores only successful package test results, so any failure you see was produced by a test binary that just ran. That asymmetry is deliberate: a stale red would be intolerable, while a stale green is at least bounded by what the go command can observe.
  • You edited a file in another package and that package's tests still print (cached) — is that a bug?
    No, if the edited package is not among that test binary's dependencies. The cache entry is keyed on the test binary, which is built from the package under test plus everything it imports. An unrelated edit leaves the binary identical, so replaying the result is correct.
  • A colleague says they never see (cached) locally. What is the likely reason?
    They probably run plain `go test` inside a package directory. Caching applies in package list mode — `go test .` or `go test ./...` — and the no-argument local-directory form is documented as not caching. Habitually passing extra non-cacheable flags, or a GOFLAGS setting containing -count=1, produces the same symptom.

saying these in an interview costs you the question

  • Thinks (cached) means the tests were skipped or had no test files
  • Believes -count=1 works by limiting the test to one execution
  • Claims failing results are cached too, so red output may be stale
  • Says the cache is per test function rather than per package
  • Reaches for go clean -cache when only test results need expiring
open as a page

Which inputs decide whether `go test` may reuse a cached result for a package?

level: middleimportance: should knowfreq 44%

basics

~20 s

Three inputs: the test binary, built from the package and its dependencies; the command line, which must use only cacheable flags; and the files under the package's source tree and the environment variables the test read.

open as a page

`go test ./...` keeps reporting (cached) for a package whose behaviour changed — how do you diagnose it?

level: seniorimportance: should knowfreq 36%

basics

~20 s

Re-run that package with go test -count=1 and compare. If the outcome changes, the test depends on something the go command never recorded: a service, a database, the clock, or a file outside the package's source root.

open as a page

How does `go test -count=1` differ from `go clean -testcache` when defeating cached results?

level: middleimportance: nice to knowfreq 29%

basics

~20 s

-count=1 affects one invocation: it takes that command out of the cacheable flag set, so nothing is reused and nothing is stored. go clean -testcache is retroactive and machine-wide, expiring every test result already in the build cache.

open as a page