Bats Bash test runner

Version 1.14.0 · source

Start here

bats test/
Run all .bats files in a dir
bats <file>.bats
Run one test file
@test "name" { ... }
Define a test
run <cmd>
Capture status and output
[ "$status" -eq 0 ]
Assert the exit status
setup() { ... }
Runs before every test
bats -r test/
Include subdirectories
bats -f <regex> test/
Run tests by name

Concepts

  1. setup_suite once per run
  2. setup_file once per file
  3. setup each test
  4. teardown each test
  5. teardown_file once per file

Running

bats <path>...
Run files or directories of .bats files
bats -r <dir>
Recurse into subdirectories
bats -f <regex> <path>
Run only tests whose name matches
bats --negative-filter <regex> <path>
Skip tests whose name matches
bats -c <path>
Count tests without running them
bats --abort <path>
Stop at the first failing test
bats -j <n> <path>
Run tests in parallel (needs GNU parallel)
bats -x <path>
Trace commands like set -x
bats --print-output-on-failure <path>
Show $output of failed tests
bats --show-output-of-passing-tests <path>
Show output of passing tests too
bats -T <path>
Add timing to each result
bats --filter-status failed <path>
Rerun failed or missing tests
bats -v / bats -h
Print version / help

Output

bats -p <path>
Pretty output (default on a terminal)
bats -t <path>
TAP output (default when piped)
bats -F junit <path>
JUnit XML for CI
bats --report-formatter junit -o <dir> <path>
Write a report file to <dir>
The output directory must already exist.
bats --no-tempdir-cleanup <path>
Keep the temp dir for debugging

Writing tests

@test "<name>" { <commands> }
Define a test
load <file>
Source a helper relative to the test file
Give the name without the .bash extension.
skip ["<reason>"]
Skip this test
bats_require_minimum_version
Fail on a too-old Bats: pass <ver>
bats_load_library <name>
Load a library from BATS_LIB_PATH
echo "# <msg>" >&3
Print a message during a test
Plain stdout and stderr show only when a test fails.
# bats test_tags=<a>,<b>
Tag the next test
# bats file_tags=<a>,<b>
Tag all tests in the file
bats --filter-tags <a>,<b> <path>
Run tests that have all these tags
Repeat the flag for OR; prefix a tag with ! to negate.
# bats test_tags=bats:focus
Run only focused tests
A focused run still exits 1 unless BATS_NO_FAIL_FOCUS_RUN=1.
bats_test_function --description <d> -- <fn>
Register tests from a loop

Run and assert

run <cmd> [args]
Capture status and output; never fails
run -<N> <cmd>
Fail unless exit status is N
run ! <cmd>
Fail unless exit status is nonzero
A bare ! in a test does not fail it reliably.
run --separate-stderr <cmd>
Split stdout and stderr
run --keep-empty-lines <cmd>
Keep blank lines in ${lines[@]}
run bats_pipe <cmd1> \| <cmd2>
Run a pipeline under run
[ "$status" -eq 0 ]
Assert exit status
[ "$output" = "<text>" ]
Assert combined output
[ "${lines[0]}" = "<text>" ]
Assert one output line
[[ "$output" == *"<part>"* ]]
Assert output contains text
$stderr / ${stderr_lines[@]}
Stderr (with --separate-stderr)

Setup and teardown

setup() { ... }
Before each test
teardown() { ... }
After each test, even on failure
setup_file() { ... }
Once before all tests in a file
teardown_file() { ... }
Once after all tests in a file
setup_suite() { ... }
Once per run, in setup_suite.bash
teardown_suite() { ... }
Once per run after the last test
$BATS_TEST_TMPDIR
Fresh temp dir for each test
$BATS_FILE_TMPDIR
Temp dir shared within a file
$BATS_SUITE_TMPDIR
Temp dir shared across the run
$BATS_TEST_DIRNAME
Directory of the test file
$BATS_TEST_TIMEOUT
Per-test timeout in seconds

Helper libraries

load 'test_helper/bats-support/load'
Load bats-support first
load 'test_helper/bats-assert/load'
Then bats-assert
assert_success / assert_failure
Check exit status after run
assert_output "<text>"
Check output (--partial, --regexp)

Recipes

A minimal test file
#!/usr/bin/env batssetup() {  cd "$BATS_TEST_TMPDIR"}@test "echo prints its argument" {  run echo hello  [ "$status" -eq 0 ]  [ "$output" = "hello" ]}
Test a script in the repo
setup() {  DIR="$(cd "$BATS_TEST_DIRNAME/.." && pwd)"  PATH="$DIR/bin:$PATH"}@test "fails without args" {  run -1 mytool  [[ "$output" == *"usage"* ]]}
CI run with JUnit report
mkdir -p reportsbats -r --report-formatter junit -o reports test/# or machine-readable on stdoutbats -r -t test/ > results.tap
Fast local loop
bats -r -j 4 --print-output-on-failure test/# rerun only what failed last timebats --filter-status failed test/