1.2.9 Golden File Testing (Test-Accept Pattern)
extraGolden-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.
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.
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).
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
2.Which properties make output a good fit for golden-file testing?
Select all that apply
Footnotes
-
Using Macros to Create Custom Verbs — Angular
ts_api_guardian_test, golden-file comparison, and the paired.accepttarget run withbazel run↩1 ↩2 ↩3 ↩4 ↩5 -
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
-
Testing — the baseline
*_testshape usingdataandargsto validate a produced artifact ↩ -
Bazel Starlark Docs on the Registry — real-world
stardoc_with_diff_testexample for checked-in generated docs and the friction when contributors must refresh them ↩1 ↩2 -
Writing Rules on Windows — cites bazel-skylib's
diff_test()as the standard "compare two files" helper ↩