Bash Project: Test Harness
A Bash test harness is a small runner that runs test functions and reports which ones passed. It turns scattered checks into one repeatable verdict.
An assert helper compares expected with actual
The helper prints pass when the two values match. Both calls match, so both lines say pass, which proves the comparison works.
assert_eq() {
if [[ "$1" == "$2" ]]; then
echo "pass: $3"
else
echo "fail: $3"
fi
}
assert_eq "5" "$((2 + 3))" "adds numbers"
word="hello"
assert_eq "HELLO" "${word^^}" "uppercases text"A runner turns test functions into ok lines
Each test function returns success for one behaviour. The runner calls each by name and prints one ok line per test.
test_add() {
[[ "$((2 + 3))" -eq 5 ]]
}
test_upper() {
local word="hello"
[[ "${word^^}" == "HELLO" ]]
}
run_test() {
if "$2"; then
echo "ok - $1"
else
echo "not ok - $1"
fi
}
run_test "adds numbers" test_add
run_test "uppercases text" test_upperA fixture is input the test creates itself
The script writes its own input file before testing it. The ok line plus the file content prove the test read the fixture it made.
printf 'name: Ada\n' > input.txt
test_fixture() {
grep -q "name: Ada" input.txt
}
if test_fixture; then
echo "ok - fixture has the expected name"
else
echo "not ok - fixture has the expected name"
fi
cat input.txtExample
pass=0
fail=0
assert_eq() {
[[ "$1" == "$2" ]]
}
test_add() {
assert_eq "5" "$((2 + 3))"
}
test_upper() {
local word="hello"
[[ "${word^^}" == "HELLO" ]]
}
test_empty() {
[[ -n "" ]]
}
run_test() {
if "$2"; then
echo "ok $3 - $1"
pass=$((pass + 1))
else
echo "not ok $3 - $1"
fail=$((fail + 1))
fi
}
echo "1..3"
run_test "adds numbers" test_add 1
run_test "uppercases text" test_upper 2
run_test "rejects empty input" test_empty 3
echo "# passed: $pass"
echo "# failed: $fail"
if [[ "$fail" -gt 0 ]]; then
exit 1
fiKey points
- Test function: a small function that returns success only when one behaviour is correct.
- Assert helper: a function that compares an expected value with an actual value.
- TAP output: a plan line like 1..3, then one ok or not ok line per test.
- Summary: counts of passed and failed tests printed after all tests run.
- Fixtures: input files a test creates before it runs, so tests do not depend on outside files.
- Isolation: each test checks one behaviour, so one failure points at one cause.
- Exit code: a runner exits nonzero when any test fails, so automation can spot failure.
In this example, a tiny runner defines an assert helper and three test functions. Each test function returns success only when one behaviour is correct, and the empty-input test fails on purpose. The runner prints a TAP plan, one ok or not ok line per test, then a summary, and it exits nonzero because one test failed.
Try it Yourself
Edit the code, then press Run.
Running is simulated
Exercise
Why does a test runner print a plan line such as 1..3 first?