| Q |
A |
| OS |
Linux (openSUSE Tumbleweed, WSL2) |
| Shell & version |
bash 5.3.9 |
| bashunit version |
0.50.0 |
Summary
tear_down_after_script is not run when set_up_before_script fails, so anything the setup hook acquired before a failing statement is leaked, with no framework-supported way to release it.
The per-test pair behaves the opposite way: tear_down is run when set_up fails. Both pairs are documented as symmetric counterparts, so the same code shape leaks at file scope and cleans up at test scope.
This matters because set_up_before_script is the only place to acquire a file-scoped resource needed for all tests in the file, and tear_down_after_script is the only place to release it. A setup hook that does more than one thing leaks whatever it already acquired as soon as any later step fails. Every author then has to duplicate the teardown logic into the setup hook's error paths, which is precisely the duplication the hook pair exists to avoid.
Current behavior
When set_up_before_script fails, bashunit reports every test in the file as failed and moves on to the next file. tear_down_after_script is not called, and nothing in the output says that it was skipped — so whatever the hook had already acquired is left behind silently.
How to reproduce
Two files, each acquiring a resource in its setup hook and then failing.
file_hooks.test.sh:
#!/usr/bin/env bash
RESOURCE=""
function set_up_before_script() {
RESOURCE="$(mktemp /tmp/bu-repro/file-scoped.XXXXXX)"
echo "acquired ${RESOURCE}"
false
}
function tear_down_after_script() {
echo "tear_down_after_script ran"
rm -f "${RESOURCE}"
}
function test_anything() {
assert_same 1 1
}
test_hooks.test.sh:
#!/usr/bin/env bash
RESOURCE=""
function set_up() {
RESOURCE="$(mktemp /tmp/bu-repro/test-scoped.XXXXXX)"
echo "acquired ${RESOURCE}"
false
}
function tear_down() {
echo "tear_down ran"
rm -f "${RESOURCE}"
}
function test_anything() {
assert_same 1 1
}
File scope — no tear_down_after_script ran in the output, and the resource survives:
$ bashunit /tmp/bu-repro/file_hooks.test.sh
Running /tmp/bu-repro/file_hooks.test.sh
✗ Error: Set up before script
acquired /tmp/bu-repro/file-scoped.WdPcUO
Tests: 1 failed, 1 total
$ ls /tmp/bu-repro/file-scoped.*
/tmp/bu-repro/file-scoped.WdPcUO
Test scope — tear_down ran, and the resource is gone:
$ bashunit /tmp/bu-repro/test_hooks.test.sh
Running /tmp/bu-repro/test_hooks.test.sh
✗ Error: Set up
acquired /tmp/bu-repro/test-scoped.oRcGOd Output: acquired /tmp/bu-repro/test-scoped.oRcGOdtear_down ran
Tests: 1 failed, 1 total
$ ls /tmp/bu-repro/test-scoped.*
ls: cannot access '/tmp/bu-repro/test-scoped.*': No such file or directory
Expected behavior
tear_down_after_script runs after a failing set_up_before_script, matching what the per-test pair already does, so a resource acquired before the failure can be released in the one place meant for it.
docs/test-files.md currently describes the hook as running "only once when all the test functions in the test file have been executed", so the current behaviour seems deliberate. I still find it surprising, though:
- The asymmetry with
set_up/tear_down is invisible at the call site. The same code shape behaves differently depending only on which of the two hook pairs it sits in.
tear_down_after_script already has to tolerate partially-built state today, because it runs after tests that potentially failed halfway through. Running it after a failed setup adds no new requirement on it.
Summary
tear_down_after_scriptis not run whenset_up_before_scriptfails, so anything the setup hook acquired before a failing statement is leaked, with no framework-supported way to release it.The per-test pair behaves the opposite way:
tear_downis run whenset_upfails. Both pairs are documented as symmetric counterparts, so the same code shape leaks at file scope and cleans up at test scope.This matters because
set_up_before_scriptis the only place to acquire a file-scoped resource needed for all tests in the file, andtear_down_after_scriptis the only place to release it. A setup hook that does more than one thing leaks whatever it already acquired as soon as any later step fails. Every author then has to duplicate the teardown logic into the setup hook's error paths, which is precisely the duplication the hook pair exists to avoid.Current behavior
When
set_up_before_scriptfails, bashunit reports every test in the file as failed and moves on to the next file.tear_down_after_scriptis not called, and nothing in the output says that it was skipped — so whatever the hook had already acquired is left behind silently.How to reproduce
Two files, each acquiring a resource in its setup hook and then failing.
file_hooks.test.sh:test_hooks.test.sh:File scope — no
tear_down_after_script ranin the output, and the resource survives:Test scope —
tear_down ran, and the resource is gone:Expected behavior
tear_down_after_scriptruns after a failingset_up_before_script, matching what the per-test pair already does, so a resource acquired before the failure can be released in the one place meant for it.docs/test-files.mdcurrently describes the hook as running "only once when all the test functions in the test file have been executed", so the current behaviour seems deliberate. I still find it surprising, though:set_up/tear_downis invisible at the call site. The same code shape behaves differently depending only on which of the two hook pairs it sits in.tear_down_after_scriptalready has to tolerate partially-built state today, because it runs after tests that potentially failed halfway through. Running it after a failed setup adds no new requirement on it.