🎓 A1Learner
🔍
0 of 45 lessons completed

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_upper

A 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.txt

Example

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
fi

Key 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?