Skip to content

Commit 147d586

Browse files
authored
Merge pull request #580 from TypedDevs/feat/579-add-output-to-failing-tests
Add output to failing tests
2 parents 59068a2 + d7b93ef commit 147d586

File tree

10 files changed

+151
-3
lines changed

10 files changed

+151
-3
lines changed

CHANGELOG.md

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,13 @@
22

33
## Unreleased
44

5+
### Added
6+
- Display test output (stdout/stderr) on failure for runtime errors
7+
- Shows captured output in an "Output:" section when tests fail with runtime errors
8+
- Helps debug test failures without manually capturing output
9+
- Enabled by default; use `--no-output-on-failure` or `BASHUNIT_SHOW_OUTPUT_ON_FAILURE=false` to disable
10+
- New CLI options: `--show-output`, `--no-output-on-failure`
11+
512
## [0.32.0](https://github.com/TypedDevs/bashunit/compare/0.31.0...0.32.0) - 2026-01-12
613

714
### Changed

docs/command-line.md

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -69,6 +69,8 @@ bashunit test tests/ --parallel --simple
6969
| `--debug [file]` | Enable shell debug mode |
7070
| `--no-output` | Suppress all output |
7171
| `--failures-only` | Only show failures |
72+
| `--show-output` | Show test output on failure (default) |
73+
| `--no-output-on-failure` | Hide test output on failure |
7274
| `--strict` | Enable strict shell mode |
7375
| `--skip-env-file` | Skip `.env` loading, use shell environment only |
7476
| `-l, --login` | Run tests in login shell context |
@@ -231,6 +233,33 @@ bashunit test tests/ --report-html report.html
231233
```
232234
:::
233235

236+
### Show Output on Failure
237+
238+
> `bashunit test --show-output`
239+
> `bashunit test --no-output-on-failure`
240+
241+
Control whether test output (stdout/stderr) is displayed when tests fail with runtime errors.
242+
243+
By default (`--show-output`), when a test fails due to a runtime error (command not found,
244+
unbound variable, permission denied, etc.), bashunit displays the captured output in an
245+
"Output:" section to help debug the failure.
246+
247+
Use `--no-output-on-failure` to suppress this output.
248+
249+
::: code-group
250+
```bash [Example]
251+
bashunit test tests/ --no-output-on-failure
252+
```
253+
```[Output with --show-output (default)]
254+
✗ Error: My test function
255+
command not found
256+
Output:
257+
Debug: Setting up test
258+
Running command: my_command
259+
/path/to/test.sh: line 5: my_command: command not found
260+
```
261+
:::
262+
234263
### Strict Mode
235264

236265
> `bashunit test --strict`

docs/configuration.md

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -347,6 +347,30 @@ BASHUNIT_FAILURES_ONLY=true
347347
```
348348
:::
349349

350+
## Show output on failure
351+
352+
> `BASHUNIT_SHOW_OUTPUT_ON_FAILURE=true|false`
353+
354+
Display captured stdout/stderr output when tests fail with runtime errors. `true` by default.
355+
356+
When a test fails due to a runtime error (command not found, unbound variable, etc.),
357+
bashunit displays the test's output in an "Output:" section to help debug the failure.
358+
359+
Similar as using `--show-output` or `--no-output-on-failure` options on the [command line](/command-line#show-output-on-failure).
360+
361+
::: code-group
362+
```[Output example]
363+
✗ Error: My test function
364+
command not found
365+
Output:
366+
Debug: Setting up test
367+
Running command: my_command
368+
```
369+
```bash [.env to disable]
370+
BASHUNIT_SHOW_OUTPUT_ON_FAILURE=false
371+
```
372+
:::
373+
350374
## Color output
351375

352376
> `NO_COLOR=1`

src/console_header.sh

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -117,6 +117,8 @@ Options:
117117
--debug [file] Enable shell debug mode
118118
--no-output Suppress all output
119119
--failures-only Only show failures (suppress passed/skipped/incomplete)
120+
--show-output Show test output on failure (default: enabled)
121+
--no-output-on-failure Hide test output on failure
120122
--strict Enable strict shell mode (set -euo pipefail)
121123
--skip-env-file Skip .env loading, use shell environment only
122124
-l, --login Run tests in login shell context

src/console_results.sh

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -335,6 +335,7 @@ function bashunit::console_results::print_snapshot_test() {
335335
function bashunit::console_results::print_error_test() {
336336
local function_name=$1
337337
local error="$2"
338+
local raw_output="${3:-}"
338339

339340
local test_name
340341
test_name=$(bashunit::helper::normalize_test_function_name "$function_name")
@@ -343,6 +344,13 @@ function bashunit::console_results::print_error_test() {
343344
line="$(printf "${_BASHUNIT_COLOR_FAILED}✗ Error${_BASHUNIT_COLOR_DEFAULT}: %s
344345
${_BASHUNIT_COLOR_FAINT}%s${_BASHUNIT_COLOR_DEFAULT}\n" "${test_name}" "${error}")"
345346

347+
if [[ -n "$raw_output" ]] && bashunit::env::is_show_output_on_failure_enabled; then
348+
line+="$(printf " %sOutput:%s\n" "${_BASHUNIT_COLOR_FAINT}" "${_BASHUNIT_COLOR_DEFAULT}")"
349+
while IFS= read -r output_line; do
350+
line+="$(printf " %s\n" "$output_line")"
351+
done <<< "$raw_output"
352+
fi
353+
346354
bashunit::state::print_line "error" "$line"
347355
}
348356

src/env.sh

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -62,6 +62,7 @@ _BASHUNIT_DEFAULT_SKIP_ENV_FILE="false"
6262
_BASHUNIT_DEFAULT_LOGIN_SHELL="false"
6363
_BASHUNIT_DEFAULT_FAILURES_ONLY="false"
6464
_BASHUNIT_DEFAULT_NO_COLOR="false"
65+
_BASHUNIT_DEFAULT_SHOW_OUTPUT_ON_FAILURE="true"
6566

6667
: "${BASHUNIT_PARALLEL_RUN:=${PARALLEL_RUN:=$_BASHUNIT_DEFAULT_PARALLEL_RUN}}"
6768
: "${BASHUNIT_SHOW_HEADER:=${SHOW_HEADER:=$_BASHUNIT_DEFAULT_SHOW_HEADER}}"
@@ -80,6 +81,7 @@ _BASHUNIT_DEFAULT_NO_COLOR="false"
8081
: "${BASHUNIT_SKIP_ENV_FILE:=${SKIP_ENV_FILE:=$_BASHUNIT_DEFAULT_SKIP_ENV_FILE}}"
8182
: "${BASHUNIT_LOGIN_SHELL:=${LOGIN_SHELL:=$_BASHUNIT_DEFAULT_LOGIN_SHELL}}"
8283
: "${BASHUNIT_FAILURES_ONLY:=${FAILURES_ONLY:=$_BASHUNIT_DEFAULT_FAILURES_ONLY}}"
84+
: "${BASHUNIT_SHOW_OUTPUT_ON_FAILURE:=${SHOW_OUTPUT_ON_FAILURE:=$_BASHUNIT_DEFAULT_SHOW_OUTPUT_ON_FAILURE}}"
8385
# Support NO_COLOR standard (https://no-color.org)
8486
if [[ -n "${NO_COLOR:-}" ]]; then
8587
BASHUNIT_NO_COLOR="true"
@@ -159,6 +161,10 @@ function bashunit::env::is_failures_only_enabled() {
159161
[[ "$BASHUNIT_FAILURES_ONLY" == "true" ]]
160162
}
161163

164+
function bashunit::env::is_show_output_on_failure_enabled() {
165+
[[ "$BASHUNIT_SHOW_OUTPUT_ON_FAILURE" == "true" ]]
166+
}
167+
162168
function bashunit::env::is_no_color_enabled() {
163169
[[ "$BASHUNIT_NO_COLOR" == "true" ]]
164170
}

src/main.sh

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -82,6 +82,12 @@ function bashunit::main::cmd_test() {
8282
--failures-only)
8383
export BASHUNIT_FAILURES_ONLY=true
8484
;;
85+
--show-output)
86+
export BASHUNIT_SHOW_OUTPUT_ON_FAILURE=true
87+
;;
88+
--no-output-on-failure)
89+
export BASHUNIT_SHOW_OUTPUT_ON_FAILURE=false
90+
;;
8591
--strict)
8692
export BASHUNIT_STRICT_MODE=true
8793
;;

src/runner.sh

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -567,9 +567,9 @@ function bashunit::runner::run_test() {
567567
elif [[ -z "$error_message" && -n "$hook_message" ]]; then
568568
error_message="$hook_message"
569569
fi
570-
bashunit::console_results::print_error_test "$failure_function" "$error_message"
570+
bashunit::console_results::print_error_test "$failure_function" "$error_message" "$runtime_output"
571571
bashunit::reports::add_test_failed "$test_file" "$failure_label" "$duration" "$total_assertions"
572-
bashunit::runner::write_failure_result_output "$test_file" "$failure_function" "$error_message"
572+
bashunit::runner::write_failure_result_output "$test_file" "$failure_function" "$error_message" "$runtime_output"
573573
bashunit::internal_log "Test error" "$failure_label" "$error_message"
574574
return
575575
fi
@@ -760,6 +760,7 @@ function bashunit::runner::write_failure_result_output() {
760760
local test_file=$1
761761
local fn_name=$2
762762
local error_msg=$3
763+
local raw_output="${4:-}"
763764

764765
local line_number
765766
line_number=$(bashunit::helper::get_function_line_number "$fn_name")
@@ -769,7 +770,12 @@ function bashunit::runner::write_failure_result_output() {
769770
test_nr=$(bashunit::state::get_tests_failed)
770771
fi
771772

772-
echo -e "$test_nr) $test_file:$line_number\n$error_msg" >> "$FAILURES_OUTPUT_PATH"
773+
local output_section=""
774+
if [[ -n "$raw_output" ]] && bashunit::env::is_show_output_on_failure_enabled; then
775+
output_section="\n Output:\n$raw_output"
776+
fi
777+
778+
echo -e "$test_nr) $test_file:$line_number\n$error_msg$output_section" >> "$FAILURES_OUTPUT_PATH"
773779
}
774780

775781
function bashunit::runner::write_skipped_result_output() {
Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
1+
#!/usr/bin/env bash
2+
set -euo pipefail
3+
4+
function set_up_before_script() {
5+
TEST_ENV_FILE="tests/acceptance/fixtures/.env.default"
6+
}
7+
8+
function test_show_output_on_failure_enabled_by_default() {
9+
local test_file=./tests/acceptance/fixtures/test_bashunit_show_output_on_failure.sh
10+
11+
local actual
12+
actual="$(./bashunit --no-parallel --env "$TEST_ENV_FILE" "$test_file" 2>&1 || true)"
13+
14+
assert_contains "Output:" "$actual"
15+
assert_contains "Debug: Starting test" "$actual"
16+
assert_contains "Info: About to run command" "$actual"
17+
}
18+
19+
function test_show_output_on_failure_disabled_via_flag() {
20+
local test_file=./tests/acceptance/fixtures/test_bashunit_show_output_on_failure.sh
21+
22+
local actual
23+
actual="$(./bashunit --no-parallel --env "$TEST_ENV_FILE" --no-output-on-failure "$test_file" 2>&1 || true)"
24+
25+
assert_not_contains "Output:" "$actual"
26+
assert_not_contains "Debug: Starting test" "$actual"
27+
}
28+
29+
function test_show_output_on_failure_disabled_via_env() {
30+
local test_file=./tests/acceptance/fixtures/test_bashunit_show_output_on_failure.sh
31+
32+
local actual
33+
actual="$(
34+
BASHUNIT_SHOW_OUTPUT_ON_FAILURE=false \
35+
./bashunit --no-parallel --env "$TEST_ENV_FILE" "$test_file" 2>&1 || true
36+
)"
37+
38+
assert_not_contains "Output:" "$actual"
39+
assert_not_contains "Debug: Starting test" "$actual"
40+
}
41+
42+
function test_show_output_flag_overrides_env() {
43+
local test_file=./tests/acceptance/fixtures/test_bashunit_show_output_on_failure.sh
44+
45+
local actual
46+
actual="$(
47+
BASHUNIT_SHOW_OUTPUT_ON_FAILURE=false \
48+
./bashunit --no-parallel --env "$TEST_ENV_FILE" --show-output "$test_file" 2>&1 || true
49+
)"
50+
51+
assert_contains "Output:" "$actual"
52+
assert_contains "Debug: Starting test" "$actual"
53+
}
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
#!/usr/bin/env bash
2+
3+
function test_with_output_before_error() {
4+
echo "Debug: Starting test"
5+
echo "Info: About to run command"
6+
nonexistent_command_xyz
7+
}

0 commit comments

Comments
 (0)