- Use Bats (`bats-core`) for shell unit tests — name test files `test/foo.bats` and run them with `bats test/`.
- Write each test as `@test "description" { ... }` and use `run` before commands whose exit code and output you want to assert.
- Assert output with `[ "$output" = "expected" ]` and exit codes with `[ "$status" -eq 0 ]` after `run`.
- Use `setup()` and `teardown()` functions in `.bats` files to create and clean up temporary fixtures for each test.
- Use `bats-assert` helpers (`assert_output`, `assert_success`, `assert_failure`) to produce clearer failure messages than raw `[ ]` checks.
- Mock external commands by creating stub executables in a temp `$PATH` directory inside `setup()` — restore `$PATH` in `teardown()`.
- Run `bats --tap test/` in CI to get TAP-compatible output that most CI systems can parse and display.