1.2.9 Golden File Testing (Test-Accept Pattern)

extra

Golden-file testing keeps an expected output under version control and asks a normal Bazel test to compare today's generated output against that checked-in baseline1,2. The clean operator split is simple: bazel test verifies, while a separate .accept target run with bazel run updates the golden when the change is intentional1. That keeps the default test path read-only, reviewable, and safe to leave in ordinary test sweeps.

Golden files: verify with tests, accept with run
A golden is the expected output stored in the repository. A normal test checks it. A separate command updates it when the new output is correct.
GOLDEN
Expected output lives in the repository
the expected result, checked in
golden.txt
ACTUAL
Fresh output comes from the build
the result produced now
actual_report.txt
operator commands
Verify — read-only
BAZEL TEST
Ordinary tests compare without writing
safe for default sweeps
bazel test //pkg:report_test
Accept — writes to the workspace
BAZEL RUN
Accept means replacing the expected file
an intentional repository change
bazel run //pkg:report.accept
Pattern: a paired *.accept target per workflow
bazel test compares actual output with the golden file and never checks in changes. bazel run //pkg:report.accept updates the checked-in expected file when the change is intended.

What The Golden Protects

A Bazel test checks source files in the repository, including test data and golden outputs2. A golden file is part of the repository state the test is asserting about.

The usual hermeticity discipline still applies. The test should depend only on declared source files, declared build outputs, and runner-provided stable resources2. If the produced output depends on timestamps, random ordering, network state, or other undeclared inputs, the test produces repeated, irrelevant churn instead of useful signal. This is the same pressure toward 2.3 Hermeticity & Sandboxing, applied to checked-in expected output.

Two Targets, Two Jobs

A good Bazel golden workflow has two labels with different responsibilities. The test target compares actual versus expected and is the thing you keep in ordinary bazel test sweeps. The accept target performs the intentional side effect of rewriting the checked-in golden when the diff is correct1.

At the low level, the standard verification shape is straightforward: a *_test target receives the artifact it should inspect through data and args3. The operator-facing extension is that the same script or macro can expose a paired runnable target, often named .accept, that switches from "verify" to "update" mode. The project exposes this update path as an executable target launched with bazel run. Bazel has no native bazel test-accept command1. If you need the runtime details of that side-effect path, they are the same ones covered in 1.1.2 bazel run & Runfiles.

A Minimal Bazel Shape

In practice the BUILD file is usually small. The compare step uses the ordinary data-flow mechanics from 0.3.3 Attributes & Semantic Roles: produce an output, pass it to a test, and also pass the checked-in golden.

load("@rules_shell//shell:sh_binary.bzl", "sh_binary")
load("@rules_shell//shell:sh_test.bzl", "sh_test")

genrule(
    name = "actual_report",
    srcs = ["source.txt"],
    outs = ["actual_report.txt"],
    cmd = "tr '[:lower:]' '[:upper:]' < $(location source.txt) > $@",
)

sh_test(
    name = "report_test",
    srcs = ["verify.sh"],
    size = "small",
    args = ["$(location :actual_report.txt)", "$(location golden.txt)"],
    data = [":actual_report.txt", "golden.txt"],
)

sh_binary(
    name = "report.accept",
    srcs = ["accept.sh"],
    args = ["$(rlocationpath :actual_report.txt)", "golden.txt"],
    data = [":actual_report.txt"],
    deps = ["@bazel_tools//tools/bash/runfiles"],
)

The golden-file-accept snippet in this repository uses exactly that shape. report_test compares a generated text file to golden.txt, while report.accept resolves the generated file from runfiles and updates the checked-in golden only when it drifted.

Where This Pattern Fits

The pattern is strongest when the output is stable, reviewable text. Angular's ts_api_guardian_test uses it for API surface checks, and ruleset documentation pipelines use the same idea to keep checked-in generated docs synchronized with current output1,4.

The pattern is weak when the output is naturally noisy. If the generated file includes nondeterministic ordering, timestamps, machine-specific paths, or other ambient state, the golden diff does not tell reviewers "the behavior changed" so much as "the environment leaked into the result"2. In those cases, fix the determinism first or choose a different assertion shape.

extra

Common Abstractions Above The Pattern

Once a repository repeats this workflow many times, teams usually stop hand-writing every compare step. Bazel's ecosystem already exposes helpers such as diff_test() for file-to-file comparisons5, and real rulesets layer domain-specific wrappers on top, such as stardoc_with_diff_test for checked-in generated API docs4. That is the natural bridge from one local golden check to 4.2 Custom Rules, Providers & Actions, and eventually to the broader developer-command surface of 3.7 Workflow Orchestration (Outside the Graph).

key takeaway

Treat golden-file testing as a two-target contract. The test target proves that today's generated output still matches the checked-in baseline. The .accept target is the explicit, reviewable escape hatch for intentional changes. Keep the output deterministic, keep the golden checked in, and keep the update path separate from ordinary bazel test.

Check your understanding · 2 questions

1.Match each golden-file target to its job:

Drag each answer onto the matching prompt, or click an answer and then click a prompt

Answers
//pkg:report_test
//pkg:report.accept

2.Which properties make output a good fit for golden-file testing?

Select all that apply

0 of 2 answered

Footnotes

  1. Using Macros to Create Custom Verbs — Angular ts_api_guardian_test, golden-file comparison, and the paired .accept target run with bazel run 1 2 3 4 5

  2. Test encyclopedia — golden outputs as checked-in source, hermetic test contract, and why undeclared inputs undermine the meaning of a test 1 2 3 4

  3. Testing — the baseline *_test shape using data and args to validate a produced artifact

  4. Bazel Starlark Docs on the Registry — real-world stardoc_with_diff_test example for checked-in generated docs and the friction when contributors must refresh them 1 2

  5. Writing Rules on Windows — cites bazel-skylib's diff_test() as the standard "compare two files" helper