Unittest is the standard test runner.
It follows the Unix philosophy with a modern touch, so it is:
- easy to use: readable elm-inspired error messages & colourized output (if applicable)
- easy to package: POSIX-compliant & MIT license
- easy to extend: with pipelines & standard Unix tools
- easy to replace: tests can be run manually (see: Brief theory of shell testing).
Its simplicity makes it useful not only for testing shell scripts but also for black-box testing of commands written in any language.
With a basic call, it searches inside tests/
directory for test_*.sh
files with test_*
functions:
$ unittest
tests/test_asserts.sh:test_assertEqual_equal_ints PASS
tests/test_asserts.sh:test_assertEqual_unequal_ints PASS
...
The result is reported to stdout. Non-zero exit code indicates, that some tests have failed. To be passed, each test should exit with 0 code.
In addition to test_*
functions, you can also define functions named:
xtest_*
- will be reported as SKIP (without error)beforeEach
andafterEach
- test preparation/cleanup code executed before/after each test functionbeforeAll
andafterAll
- test preparation/cleanup code executed once per file, before/after all test functions.
Tests can call the test
function, which extends the test
command with a readable error report (to stderr):
$ unittest
...
tests/test_number_assertions.sh:test_lessequal_mixed2 PASS
-- FAILED TEST [tests/test_output.sh:test_status_pass]
I expected:
test '0' '-eq' '10'
to be true, but the result was false.
tests/test_output.sh:test_status_pass FAIL
tests/test_output.sh:test_status_fail PASS
...
For example, tests see files inside the tests directory.
The instruction is for Linux. On different OSes, you may need to use different commands
-
Download latest stable release from GitHub:
wget https://github.com/macie/unittest.sh/releases/latest/download/unittest
-
(OPTIONAL) Verify downloading:
wget https://github.com/macie/unittest.sh/releases/latest/download/unittest.sha256sum sha256sum -c unittest.sha256sum
-
Set execute permission:
chmod +x unittest
-
Move to directory from
PATH
environment variable:mv unittest /usr/local/bin/
git clone git@github.com:macie/unittest.sh.git
cd unittest.sh
make && make install
Use make
(GNU or BSD):
make
- run checksmake test
- run testmake check
- perform static code analysismake install
- install in/usr/local/bin
make dist
- prepare for distributingmake clean
- remove distributed artifactsmake release
- tag latest commit as a new releasemake info
- print system info (useful for debugging).
unittest is versioned according to the scheme YY.0M.MICRO
(calendar versioning). Releases are tagged in Git.
Robert Lehmann created a list of the most popular shell testing tools. The main difference between them and this project: unittest is idiomatic to Unix. It uses only POSIX commands and standard output in a simple format. So the result can be easily transformed or extended.
The only comparable alternative is to run tests manually (as described below).
From its beginning, Unix is test-friendly. The simplest shell test is based on exit codes:
$ command && echo PASS || echo FAIL
PASS
Commands which don't crash by default are relatively common. Many practical tests verify what command do (with a little help from standard Unix tools):
$ [ -n "$(command)" ] && echo PASS || echo FAIL
PASS
$ command | grep 'expected output' && echo PASS || echo FAIL
PASS
Tests for further usage can be wrapped by function inside shell script:
$ test_command.sh <<EOF
test_run() {
command
}
test_output() {
command | grep 'expected output'
}
EOF
The exit code of the function is the same as the exit code of the last command in the function, so after sourcing we can use them as before:
$ . test_command.sh
$ test_output && echo PASS || echo FAIL
PASS
Finally, all tests from a file can be run with some grep
and loop. And this is basically unittest
.