1.2.7 Test Output & Debugging

When a test fails, Bazel gives you two observation channels: what reaches the terminal during bazel test, and what it saves under bazel-testlogs for later inspection. --test_output controls the first channel, while --test_summary controls the rollup at the end.1,2

Where does test output go?
The terminal is a view of the run. bazel-testlogs keeps files you can inspect later.
ONE TEST RUN, TWO PLACES TO LOOK
Bazel runs the test and reports the result
Choose the terminal detail. Saved files remain on disk.
bazel test //pkg:my_test --test_output=errors
1 · TERMINAL
Control what you see now
--test_output selects the amount and timing
summary short result and log path
errors output from failed tests
all output from every test
streamed Live output. Tests run one at a time.
2 · BAZEL-TESTLOGS
Open files after the run
logs, XML results, and undeclared test outputs live under the target
ONE RUN
bazel-testlogs/<package>/<target>/ test.log · test.xml · test.outputs/
WITH --runs_per_test=3
bazel-testlogs/<package>/<target>/ run_1_of_3/test.log · test.xml · test.outputs/ run_2_of_3/... run_3_of_3/...
Inside each repeated execution, TEST_RUN_NUMBER and TEST_RANDOM_SEED identify that run from 1 to N.
Use the terminal for immediate feedback. Use bazel-testlogs when you need the saved evidence.

Terminal Output Is Not The Saved Log

On Bazel 9, the default --test_output mode is summary, not errors.1,2 That means a failing test usually prints a short status line and the path to the saved log instead of inlining the whole stdout/stderr stream.

The test-output-debugging snippet reproduces the default behavior:

$ bazel test //:fail_test
FAIL: //:fail_test (Exit 1) (see .../testlogs/fail_test/test.log)
...
//:fail_test FAILED in 0.5s
  .../testlogs/fail_test/test.log

The shorter path you normally open by hand is the bazel-testlogs convenience symlink, which points at the underlying .../bazel-out/.../testlogs directory in the output tree.3 In the snippet's root-package case, that means bazel-testlogs/fail_test/test.log. That is the testing-specific slice of the output tree introduced in 0.1.4 Output Root.

Pick the right --test_output mode

summary is the quiet default: status lines, plus the log-file path for failures.1,2 errors still buffers output per test, but only prints stdout/stderr for failed tests after each failing test completes.1,2 all does the same for passing tests too, which is useful when a test passes unexpectedly and you still need to see its diagnostics.1,2

The same snippet with --test_output=all prints the test body directly in the terminal:

INFO: From Testing //:pass_test:
==================== Test output for //:pass_test:
pass stdout
pass stderr

streamed is different: Bazel forwards stdout/stderr in real time instead of replaying it after the test finishes.1,4 That is the right mode for a hanging test, a slow integration test that prints progress, or a Java test started under --java_debug, which implies --test_output=streamed.1

The trade-off is explicit. With --test_output=streamed --cache_test_results=no, the snippet prints Bazel's own warning first:

WARNING: Streamed test output requested. All tests will be run without sharding, one at a time
pass stdout
pass stderr

So streamed buys immediacy by giving up parallel test execution.

--test_summary changes the rollup, not the log body

--test_output and --test_summary control separate parts of the output.1,2 You can keep --test_output=errors so failures inline their logs, while independently switching the final summary between short, terse, and none.1,2

The more detailed summary modes depend on structured test output. Bazel passes the XML destination through XML_OUTPUT_FILE, and --test_summary=testcase becomes useful only when the runner writes valid JUnit XML there.1,5,6 That is why test-case-level reporting and richer artifacts live in the same bazel-testlogs tree as test.log.5,6 Coverage is a separate next step in 1.2.8 Coverage, but it builds on the same idea: the interesting debugging artifacts are on disk even when the terminal view stays brief.

extra

Tests can also publish extra debugging artifacts without declaring normal build outputs. Files written to TEST_UNDECLARED_OUTPUTS_DIR are collected under bazel-testlogs, which is useful for screenshots, extra logs, or other one-off evidence you want only when a test runs.5,6

Use --runs_per_test to prove a fix

--runs_per_test=N tells Bazel to execute each selected test N times as separate test runs.1,2 Unlike the retry policy from 1.2.3 Test Tags, which uses flaky = True and --flaky_test_attempts to tolerate transient failures, --runs_per_test is an explicit debugging tool for reproducing or disproving flakiness.1,7

The snippet with --runs_per_test=3 --test_output=all produces real repeated executions:

INFO: From Testing //:run_number_test (run 2 of 3):
==================== Test output for //:run_number_test (run 2 of 3):
run_number=2
random_seed=2
...
//:run_number_test PASSED in 0.3s
  Stats over 3 runs: max = 0.3s, min = 0.3s, avg = 0.3s, dev = 0.0s

Bazel keeps the saved files for each execution in a separate directory under the target's testlogs directory: run_1_of_3, run_2_of_3, and run_3_of_3 for --runs_per_test=3. Inside the test process, TEST_RUN_NUMBER and TEST_RANDOM_SEED identify the current execution, both starting at 1 for the first run.5 That lets a randomized test reproduce a particular run, and it lets you match a message from the test to the corresponding saved directory.

The flag also accepts regex@number, so you can rerun only part of a larger request, and when the count is greater than 1 Bazel disables test-result caching automatically.1,2 Repeated runs are fresh executions rather than cached replays from 1.2.1 Test Caching.

extra

If you want Bazel to classify mixed PASS/FAIL reruns as flaky instead of failed, add --runs_per_test_detects_flakes. Without it, any failing run makes the target fail.1

If the log already tells you the failing assertion, this is usually enough. If it only gives you a symptom and the real issue is deeper in analysis, sandboxing, or action execution, continue in 5.6 Debugging.

key takeaway

Think in layers. --test_output decides what you see now, bazel-testlogs keeps the full artifacts for later, --test_summary changes the rollup, and --runs_per_test turns one suspect test into a repeatable debugging experiment. Start quiet with summary or errors, switch to streamed when you need live progress, and reach for repeated runs when you need proof rather than a hunch.

Check your understanding · 3 questions

1.Match each --test_output mode to what it shows on the terminal:

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

Answers
summary
errors
all
streamed

2.What is the trade-off of using --test_output=streamed?

Select one answer

3.True or false: how do --runs_per_test and --flaky_test_attempts relate?

Choose True or False for each sentence

--runs_per_test=5 and --flaky_test_attempts=5 do the same thing.
--runs_per_test performs N fresh runs for debugging, while --flaky_test_attempts retries only after a failure.
--runs_per_test only works with streamed output, while --flaky_test_attempts works with any mode.
--runs_per_test caches all runs, while --flaky_test_attempts never caches.
0 of 3 answered

Footnotes

  1. Command-Line Reference — canonical --test_output, --test_summary, --runs_per_test, --runs_per_test_detects_flakes, and --java_debug semantics 1 2 3 4 5 6 7 8 9 10 11 12 13 14

  2. Bazel Testing Tips — operator-focused explanation of summary vs errors vs all vs streamed, the separate --test_summary axis, and --runs_per_test disabling caching 1 2 3 4 5 6 7 8 9 10

  3. Output Directory Layoutbazel-testlogs convenience symlink and the underlying .../testlogs directory in Bazel's output tree

  4. Bazel flag cheat sheet — practical pairing of --test_filter with --test_output=streamed during local debugging 1 2

  5. Test encyclopediaTEST_RUN_NUMBER, TEST_RANDOM_SEED, XML_OUTPUT_FILE, and undeclared test outputs under bazel-testlogs 1 2 3 4

  6. Bazel Training 101 (Part 16): Coverage and Test Cases--test_summary=testcase, JUnit XML in bazel-testlogs, and undeclared outputs for extra test artifacts 1 2 3

  7. Bazel Training 101 (Part 17): Flakiness — contrast between --runs_per_test for verification and --flaky_test_attempts or flaky = True for retry policy