Shunit2
shUnit2 is a xUnit based unit test framework for Bourne based shell scripts.
Install / Use
/learn @kward/Shunit2README
shUnit2
shUnit2 is a xUnit unit test framework for Bourne based shell scripts, and it is designed to work in a similar manner to JUnit, PyUnit, etc.. If you have ever had the desire to write a unit test for a shell script, shUnit2 can do the job.
Information
If you want to jump straight in, scroll down to the Quickstart section.
Background
shUnit2 was originally developed to provide a consistent testing solution for log4sh, a shell based logging framework similar to log4j. During the development of that product, a repeated problem of having things work just fine under one shell (/bin/bash on Linux to be specific), and then not working under another shell (/bin/sh on Solaris) kept coming up. Although several simple tests were run, they were not adequate and did not catch some corner cases. The decision was finally made to write a proper unit test framework after multiple brown-bag releases were made. Research was done to look for an existing product that met the testing requirements, but no adequate product was found.
Tested software
Tested Operating Systems (varies over time)
| OS | Support | Verified | | ----------------------------------- | --------- | ---------- | | Ubuntu Linux (14.04.05 LTS) | Travis CI | continuous | | macOS High Sierra (10.13.3) | Travis CI | continuous | | FreeBSD | user | unknown | | Solaris 8, 9, 10 (inc. OpenSolaris) | user | unknown | | Cygwin | user | unknown |
Tested Shells
- Bourne Shell (sh)
- BASH - GNU Bourne Again SHell (bash)
- DASH - Debian Almquist Shell (dash)
- Korn Shell - AT&T version of the Korn shell (ksh)
- mksh - MirBSD Korn Shell (mksh)
- zsh - Zsh (zsh) (since 2.1.2) please see the Zsh shell errata for more information
See the appropriate Release Notes for this release (doc/RELEASE_NOTES-X.X.X.txt) for the list of actual versions tested.
Credits / Contributors
A list of contributors to shUnit2 can be found in doc/contributors.md. Many thanks go out to all those who have contributed to make this a better tool.
shUnit2 is the original product of many hours of work by Kate Ward, the primary author of the code. For related software, check out https://github.com/kward.
Feedback
Feedback is most certainly welcome for this document. Send your questions, comments, and criticisms via the shunit2-users forum (created 2018-12-09), or file an issue via https://github.com/kward/shunit2/issues.
Quickstart
Overview
shUnit is designed to execute all functions in your unit test that are prefixed test. It will walk through them one by one in the order they appear. In addition, there are some helper functions that will be called to setup and cleanup the environment.
The overall flow looks like this.
sequenceDiagram
participant unit_test as unit test
unit_test-->>shUnit2: shUnit2 loaded from unit test
note over unit_test,shUnit2: shUnit2 identifies test*() functions
shUnit2->>unit_test: oneTimeSetUp()
loop for each test function
shUnit2->>unit_test: setUp()
shUnit2->>unit_test: testSomeFunction()
unit_test-->>script: code called from testSomeFunction()
shUnit2->>unit_test: tearDown()
end
shUnit2->>unit_test: oneTimeTearDown()
Your first test
This section will give a very quick start to running unit tests with shUnit2. More information is located in later sections.
Here is a quick sample script to show how easy it is to write a unit test in shell. Note: the script as it stands expects that you are running it from the examples directory.
#! /bin/sh
# file: examples/equality_test.sh
testEquality() {
assertEquals 1 1
}
# Load shUnit2.
. ../shunit2
Running the unit test should give results similar to the following.
$ cd examples
$ ./equality_test.sh
testEquality
Ran 1 test.
OK
W00t! You've just run your first successful unit test.
Analysis
So, what just happened? Quite a bit really, and it all happened simply by sourcing the shunit2 library. The basic functionality for the script above goes like this:
When shUnit2 is sourced, it will walk through any functions defined whose name starts with the string test, and add those to an internal list of tests to execute. Once a list of test functions to be run has been determined, shunit2 will go to work.
Before any tests are executed, shUnit2 again looks for a function, this time one named oneTimeSetUp(). If it exists, it will be run. This function is normally used to setup the environment for all tests to be run. Things like creating directories for output or setting environment variables are good to place here. Just so you know, you can also declare a corresponding function named oneTimeTearDown() function that does the same thing, but once all the tests have been completed. It is good for removing temporary directories, etc.
shUnit2 is now ready to run tests.
- Before doing so though, it again looks for another function that might be declared, one named
setUp(). If the function exists, it will be run before each test. It is good for resetting the environment so that each test starts with a clean slate. - At this stage, the first test is finally run. The success of the test is recorded for a report that will be generated later.
- After the test is run, shUnit2 looks for a final function that might be declared, one named
tearDown(). If it exists, it will be run after each test. It is a good place for cleaning up after each test, maybe doing things like removing files that were created, or removing directories. This set of steps,setUp() > test() > tearDown(), is repeated for all of the available tests.
Once all the work is done, shUnit2 will generate the nice report you saw above. A summary of all the successes and failures will be given so that you know how well your code is doing.
We should now try adding a test that fails. Change your unit test to look like this.
#! /bin/sh
# file: examples/party_test.sh
testEquality() {
assertEquals 1 1
}
testPartyLikeItIs1999() {
year=`date '+%Y'`
assertEquals "It's not 1999 :-(" '1999' "${year}"
}
# Load shUnit2.
. ../shunit2
So, what did you get? I guess it told you that this isn't 1999. Bummer, eh? Hopefully, you noticed a couple of things that were different about the second test. First, we added an optional message that the user will see if the assert fails. Second, we did comparisons of strings instead of integers as in the first test. It doesn't matter whether you are testing for equality of strings or integers. Both work equally well with shUnit2.
Hopefully, this is enough to get you started with unit testing. If you want a ton more examples, take a look at the tests provided with log4sh or shFlags. Both provide excellent examples of more advanced usage. shUnit2 was after all written to meet the unit testing need that log4sh had.
If you are using distribution packaged shUnit2 which is accessible from /usr/bin/shunit2 such as Debian, you can load shUnit2 without specifying its path. So the last 2 lines in the above can be replaced by:
# Load shUnit2.
. shunit2
Function Reference
General Info
Any string values passed should be properly quoted -- they should be surrounded by single-quote (') or double-quote (") characters -- so that the shell will properly parse them.
Asserts
Equality
assertEquals [message] expected actual
Asserts that expected and actual are equal to one another. The expected and actual values can be either strings or integer values as both will be treated as strings. The message is optional, and must be quoted.
assertNotEquals [message] unexpected actual
Asserts that unexpected and actual are not equal to one another. The unexpected and actual values can be either strings or integer values as both will be treated as strings. The message is optional, and must be quoted.
assertSame [message] expected actual
{deprecated} This function is functionally equivalent to assertEquals.
assertNotSame [message] unexpected actual
{deprecated} This function is functionally equivalent to assertNotEquals.
Containment
assertContains [message] container content
Asserts that container contains content. The container and content values can be either strings or integer values as both will be treated as strings. The message is optional, and must be quoted.
assertNotContains [message] container content
Asserts that container does not contain content. The container and content values can be either strings or integer values as both will be treated as strings. The message is optional, and must be quoted.
Null
assertNull [message] value
Asserts that value is null, or in shell terms, a zero-length string. The value must be a string as an integer value does not translate into a zero-length string. The message is optional, and must be quoted.
assertNotNull [message] value
Asserts that value is not null, or in shell terms, a non-empty string. The value may be a string or an integer as the latter will be parsed as a non-empty string value. The message is optional, and must be quoted.
Truth
assertTrue [message] condition
Asserts that a given shell test condition is true. The condition can be as simple as a shell
Related Skills
node-connect
346.8kDiagnose OpenClaw node connection and pairing failures for Android, iOS, and macOS companion apps
frontend-design
107.6kCreate distinctive, production-grade frontend interfaces with high design quality. Use this skill when the user asks to build web components, pages, or applications. Generates creative, polished code that avoids generic AI aesthetics.
openai-whisper-api
346.8kTranscribe audio via OpenAI Audio Transcriptions API (Whisper).
qqbot-media
346.8kQQBot 富媒体收发能力。使用 <qqmedia> 标签,系统根据文件扩展名自动识别类型(图片/语音/视频/文件)。
