Repository navigation
Description
Background
Writing a custom assertion today is a three-part contract, spelled out under "Best practices" in docs/custom-asserts.md:
function assert_positive_number() {
local actual="$1"
if [ "$actual" -le 0 ]; then
bashunit::assertion_failed "positive number" "$actual"
return
fi
bashunit::assertion_passed
}There are two ways to get that wrong, and the framework catches neither:
- Forget the
returnafterbashunit::assertion_failed, and both the failed and the passed counter bump for a single assertion. - Forget
bashunit::assertion_passed, and the test registers zero assertions, so it silently lands as risky (src/runner.sh:1411) or as a failure under--fail-on-risky.
Separately, a custom assertion cannot name itself in its own failure block. bashunit::assert::fail_with (src/assert.sh:52) already takes a label as $1, but the public bashunit::assertion_failed (src/bashunit.sh:7) hard-codes it to "", so the output always shows the test name.
Proposal
1. bashunit::assert_that <expected> <actual> <cmd> [args...]
Runs the command and marks the assertion passed or failed accordingly. No eval, no return dance, Bash 3.0 safe:
function bashunit::assert_that() {
bashunit::assert::should_skip && return 0
local expected=$1
local actual=$2
shift 2
if "$@"; then
bashunit::state::add_assertions_passed
return 0
fi
bashunit::assert::fail_with "" "$expected" "but got " "$actual"
return 1
}The example above collapses to one line, with both counters impossible to desync:
function assert_positive_number() {
bashunit::assert_that "positive number" "$1" test "$1" -gt 0
}2. Optional label on bashunit::assertion_failed
Accept a fourth argument and forward it into fail_with's existing $1, so a custom assertion can label its own failure. Backward compatible.
3. Docs
Update docs/custom-asserts.md with the new form, and mention --boot / BASHUNIT_BOOTSTRAP as the way to load a shared custom-assert file once. Every current example sources it from set_up, which is per test file.
Notes
Entirely additive. The existing three-part form keeps working, so no deprecation is needed.
Part of a custom-asserts DX set, each sized as its own PR:
- Custom asserts: a one-call assertion helper and self-labelled failures #915 one-call helper, self-labelled failures, docs
- Custom asserts: a supported way to test that an assertion fails #916 testing a custom assertion without reaching into internals
- Custom asserts: a composed assertion should count and report once #917 composed assertions count and report once
- Custom asserts: list a project's own assertions in
bashunit doc#918 listing project assertions inbashunit doc
Metadata
Metadata
Assignees
Labels
Type
Projects
- StatusShow more project fieldsDone
Activity
Chemaclass commentedon Jul 29, 2026
Done in #919 (merged).