go test ./... -update is a common golden-test update convention, but -update is not built into go test. The repository’s test code defines that flag and uses it to decide whether to compare against or overwrite .golden files.

A typical implementation looks like:

 1var update = flag.Bool("update", false, "update golden files")
 2
 3//go:embed testdata/example.golden
 4var exampleGolden []byte
 5
 6func TestSomething(t *testing.T) {
 7    got := generateOutput()
 8
 9    golden := "testdata/example.golden"
10
11    if *update {
12        if err := os.WriteFile(golden, got, 0644); err != nil {
13            t.Fatal(err)
14        }
15        // When updating, we must read back from disk to get the new state
16        // because the embedded variable exampleGolden won't change at runtime.
17        want, err := os.ReadFile(golden)
18        if err != nil {
19            t.Fatal(err)
20        }
21        if !bytes.Equal(got, want) {
22            t.Errorf("output differs from golden file")
23        }
24        return
25    }
26
27    want := exampleGolden
28
29    if !bytes.Equal(got, want) {
30        t.Errorf("output differs from golden file")
31    }
32}

So normally:

1go test ./...

does approximately:

 1generate current output
 2 3 4read testdata/foo.golden
 5 6 7compare expected ↔ actual
 8 9        ├── same → PASS
10        └── different → FAIL

Whereas:

1go test ./... -update

does:

1generate current output
234write output → testdata/foo.golden
567golden file now represents the new expected output

Why -update reaches the tests

Go’s test binary accepts flags registered by the test package. For example:

1var update = flag.Bool("update", false, "update golden files")

When you run:

1go test ./... -update

go test builds each package’s test binary and effectively invokes it with the relevant test arguments. The registered update flag is then available as:

1*update

inside the tests.

Sometimes you’ll instead see:

1go test ./... -args -update

-args explicitly says “everything after this goes to the generated test binary.” For a flag that go test doesn’t itself consume, the shorter:

1go test ./... -update

often works as well.

More realistic golden-test pattern

Many projects have a helper along these lines:

 1func golden(t *testing.T, name string, got []byte) {
 2    t.Helper()
 3
 4    filename := filepath.Join("testdata", name+".golden")
 5
 6    if *update {
 7        if err := os.WriteFile(filename, got, 0644); err != nil {
 8            t.Fatal(err)
 9        }
10    }
11
12    want, err := os.ReadFile(filename)
13    if err != nil {
14        t.Fatal(err)
15    }
16
17    if diff := cmp.Diff(string(want), string(got)); diff != "" {
18        t.Errorf("golden mismatch (-want +got):\n%s", diff)
19    }
20}

Then tests simply do:

1func TestRender(t *testing.T) {
2    got := renderSomething()
3    golden(t, "render_something", []byte(got))
4}

Running:

1go test ./... -update

regenerates:

1testdata/render_something.golden

from renderSomething().

The important implication is that the test implementation itself is the generator. There usually isn’t a separate .golden generation program. -update just switches the assertion helper from roughly:

“compare this result with the checked-in expected result”

to:

“make this result the new checked-in expected result.”

If you’re asking about one of your particular GitHub repos, I can trace the exact -update implementation and show precisely which code generates each .golden file.

Integrating with fs.FS and txtar

When working with more complex testing scenarios—especially involving file system abstractions (fs.FS) and the txtar format for multi-file fixtures—the -update convention remains powerful.

Instead of reading and writing individual .golden files from disk, you can use -update to rewrite the actual .txtar archives that serve as your test inputs and expected outputs.

For instance, if your tests use fs.WalkDir over a structured directory of txtar test cases, and inject an in-memory MapFS or MockFS for the code under test to operate on, you can capture the resulting virtual filesystem state. If -update is true, you can bundle that in-memory state back into the txtar archive format and write it back out to the real testdata/ directory, updating the expected files inline.

This aligns perfectly with agentic coding practices by ensuring complex inputs and expected outputs are clearly defined and easily regenerated, while the actual testing logic remains isolated through fs.FS interfaces rather than coupled to os functions.

The Case for go:embed

While os.ReadFile works fine for simple local testing, I strongly recommend using go:embed to read your test fixture files during assertions whenever possible (as shown in the first example).

Embedding the test data directly into the test binary substantially reduces file path resolution failures, especially when tests are run from different working directories or within CI/CD pipelines and isolated agent environments. It guarantees that the expected data is always packaged alongside the test that requires it.

In practice, you use -update and os.WriteFile to write the files to disk, and your test assertions (when not updating) read the expected state from the embedded filesystem block, ensuring rock-solid read reliability.