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
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.
For the common "one broken test, tight local loop" workflow, start here:
bazel test //pkg:my_test \
--test_filter=SomeCase \
--test_output=streamed \
--cache_test_results=no
--test_filter narrows the runner to the interesting case when the test framework supports it, --test_output=streamed shows progress live, and --cache_test_results=no prevents 1.2.1 Test Caching from replaying a stale pass while you debug.2,4
--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.
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.
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.
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
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
Footnotes
-
Command-Line Reference — canonical
--test_output,--test_summary,--runs_per_test,--runs_per_test_detects_flakes, and--java_debugsemantics ↩1 ↩2 ↩3 ↩4 ↩5 ↩6 ↩7 ↩8 ↩9 ↩10 ↩11 ↩12 ↩13 ↩14 -
Bazel Testing Tips — operator-focused explanation of
summaryvserrorsvsallvsstreamed, the separate--test_summaryaxis, and--runs_per_testdisabling caching ↩1 ↩2 ↩3 ↩4 ↩5 ↩6 ↩7 ↩8 ↩9 ↩10 -
Output Directory Layout —
bazel-testlogsconvenience symlink and the underlying.../testlogsdirectory in Bazel's output tree ↩ -
Bazel flag cheat sheet — practical pairing of
--test_filterwith--test_output=streamedduring local debugging ↩1 ↩2 -
Test encyclopedia —
TEST_RUN_NUMBER,TEST_RANDOM_SEED,XML_OUTPUT_FILE, and undeclared test outputs underbazel-testlogs↩1 ↩2 ↩3 ↩4 -
Bazel Training 101 (Part 16): Coverage and Test Cases —
--test_summary=testcase, JUnit XML inbazel-testlogs, and undeclared outputs for extra test artifacts ↩1 ↩2 ↩3 -
Bazel Training 101 (Part 17): Flakiness — contrast between
--runs_per_testfor verification and--flaky_test_attemptsorflaky = Truefor retry policy ↩