Skip to content

test.sh

Eugene Lazutkin edited this page Mar 21, 2026 · 2 revisions

test.sh

Built-in test harness for writing automated tests. Follows the same conventions as library modules: include guard, set -euo pipefail, test:: namespace. Auto-loads ansi.sh for colored pass/fail output.

Bash requirement: 4.0+ Dependencies: ansi.sh (auto-loaded) Include guard: test::equal

Usage

#!/usr/bin/env bash
set -euo pipefail

source "$(dirname "$(realpath "$0")")/../test.sh"
source "$test_lib_dir/string.sh"

echo "string.sh"

test::equal "$(string::make_pad 3 '.')" "..." "make_pad dots"
test::equal "$(string::pad 'hi' 10 l)"  "hi        " "pad left"
test::contains "hello world" "world" "contains world"

test::done

Globals

  • test_lib_dir — absolute path to the library root directory. Use this to source modules under test.
  • test_pass — number of passed assertions so far.
  • test_fail — number of failed assertions so far.
  • test_total — total number of assertions so far.
  • test_name — current test group label (set via test::name).

Assertions

test::equal ACTUAL EXPECTED [MSG]

Assert that ACTUAL equals EXPECTED (string comparison).

test::equal "$(string::length 'hello')" "5" "length of hello"

test::not_equal ACTUAL EXPECTED [MSG]

Assert that ACTUAL does not equal EXPECTED.

test::not_equal "${RED:-}" "" "RED is defined"

test::match ACTUAL PATTERN [MSG]

Assert that ACTUAL matches the extended regex PATTERN.

test::match "$(ansi::make bold)" $'\e' "contains escape byte"

test::contains HAYSTACK NEEDLE [MSG]

Assert that HAYSTACK contains the substring NEEDLE (uses grep -qF).

test::contains "${args_cleaned[*]}" "foo" "positional arg present"

test::ok [MSG]

Unconditional pass. Useful for marking that a code path was reached.

test::ok "sourced without error"

test::fail_ [MSG]

Unconditional fail. The trailing underscore avoids collision with a potential Bash builtin.

test::fail_ "should not reach here"

Lifecycle

test::name LABEL

Set a label for the current test group. This is informational only.

test::done

Print a summary of pass/fail counts and exit. Exits 0 if all assertions passed, 1 if any failed. Every test file must call this at the end.

Test runner

The runner script tests/run.sh discovers all tests/test-*.sh files and executes each in a separate bash process. It reports which files passed or failed and exits 1 if any file failed.

bash tests/run.sh

Writing a new test file

  1. Create tests/test-<module>.sh.
  2. Source test.sh relative to the file location.
  3. Source the module(s) under test via $test_lib_dir.
  4. Write assertions.
  5. Call test::done at the end.
#!/usr/bin/env bash
set -euo pipefail

script_dir="$(dirname "$(realpath "$0")")"
source "$script_dir/../test.sh"
source "$test_lib_dir/box.sh"

echo "box.sh"

test::equal "$(box::get_width 'hello')" "5" "width of single line"
test::equal "$(box::get_height $'a\nb')" "2" "height of two lines"

test::done

Clone this wiki locally