Skip to content
This repository was archived by the owner on Jun 21, 2020. It is now read-only.

Bash: Unit Testing

Scot P. Floess edited this page Jan 7, 2018 · 22 revisions

Unit Testing

Without a doubt, we discovered writing bash functions to be very simple - testing them for "the long haul" much more challenging. Our "knee-jerk" reaction was to write "one off" tests - but that doesn't ensure our quality should we need to make changes. With that said, we've provided a test harness vary similar in nature to JUnit in what we call the test-utils.sh framework. If you are familiar with JUnit this should "hopefully" be obvious. We do unit test (or try to) all our functions as can be found here.

How To

Asserts

Asserts allow one to test certain conditions and fail if those conditions are not met (as in exit with a 1 as the status code). Assert function names will resemble the intent and can be found in the script test-utils.sh.

assert-equals

This function accepts two parameters and ensures those parameters are equal.

Example

# Will fail (exit code 1)
assert-equals "foo bar" "alpha beta"

# Will pass (exit code 0)
assert-equals "theta force" "theta force"

# Pass/fail depends on the values of ${foo} and ${bar}
assert-equals "${foo}" "${bar}"

assert-not-equals

This function accepts two parameters and ensures those parameters are not equal.

Example

# Will pass (exit code 0)
assert-not-equals "foo bar" "alpha beta"

# Will fail (exit code 1)
assert-not-equals "theta force" "theta force"

# Pass/fail depends on the values of ${foo} and ${bar}
assert-not-equals "${foo}" "${bar}"

assert-blank

This function accepts 1 parameter and ensures it is blank (empty string).

Example

# Will pass (exit code 0)
assert-blank ""

# Will fail (exit code 1)
assert-blank "      "

# Pass/fail depends on the values of ${foo}
assert-blank "${foo}"

assert-not-blank

This function accepts 1 parameter and ensures it is not blank (empty string).

Example

# Will pass (exit code 0)
assert-not-blank ""

# Will fail (exit code 1)
assert-not-blank "    "
assert-not-blank "Hello world"

# Pass/fail depends on the values of ${foo}
assert-blank "${foo}"

assert-file-not-exists

This function accepts 1 parameter and ensures a file exists. If the parameter is blank, it will fail.

Example

# Will pass (exit code 0)
rm /tmp/foo
assert-file-not-exists /tmp/foo

# Will fail (exit code 1)
touch /tmp/foo
assert-file-not-exists /tmp/foo

# Will fail as the param is blank
assert-file-not-exists ""

# Pass/fail depends on the values of ${foo}
assert-file-not-exists "${foo}"

assert-file-exists

This function accepts 1 parameter and ensures a file exists. If the parameter is blank, it will fail.

Example

# Will pass (exit code 0)
touch /tmp/foo
assert-file-exists /tmp/foo

# Will fail (exit code 1)
rm /tmp/foo
assert-file-exists /tmp/foo

# Will fail as this is a dir
assert-file-exists /tmp

# Will fail as the param is blank
assert-file-exists ""

# Pass/fail depends on the values of ${foo}
assert-file-exists "${foo}"

assert-success

This function considers all parameters as something to execute and examines the exit code for success (an exit code of 0 is success).

Example

myTestFunction {
    if [ $# -lt 1]
    then
        exit 1
    elif [ -d $1 ]
    then
        exit 0
    fi

    exit 1
}

# Will pass (exit code 0)
mkdir /tmp/foo
assert-sucess myTestFunction /tmp/foo

# Will fail (exit code 1)
rmdir /tmp/foo
assert-success myTestFunction /tmp/foo

# Will fail (exit code 1)
assert-success myTestFunction

assert-failure

This function considers all parameters as something to execute and examines the exit code for failure (an exit code other than 0 is a failure that translates to this function returning success).

Example

myTestFunction() {
    if [ $# -lt 1]
    then
        exit 1
    elif [ -d $1 ]
    then
        exit 0
    fi

    exit 1
}

# Will fail (exit code 1)
mkdir /tmp/foo
assert-failure myTestFunction /tmp/foo

# Will pass (exit code o)
rmdir /tmp/foo
assert-failure myTestFunction /tmp/foo

# Will pass (exit code 0)
assert-failure myTestFunction

Unit Tests

A unit test represents a functional test to prove or disprove something works as designed. Should your code change, either the unit test will continue to pass or the unit test itself may require a change.

unit-test-should-pass

This function performs a unit test and expects the test to succeed with an exit code of 0.

Example
test-file-exists-for-success() {
    touch /tmp/tmp-foo

    assert-file-exists /tmp/tmp-foo
    
    rm /tmp/tmp-foo

    assert-file-not-exists /tmp/tmp-foo
}

test-file-exists-for-failure() {
    rm /tmp/tmp-foo

    assert-file-exists /tmp/tmp-foo
}

# Will pass
unit-test-should-pass test-file-exists-for-success

# Will fail
unit-test-should-pass test-file-exists-for-failure

unit-test-should-fail

This function performs a unit test and expects the test to fail with an exit code of 1.

Example
test-file-exists-for-success() {
    touch /tmp/tmp-foo

    assert-file-exists /tmp/tmp-foo
    
    rm /tmp/tmp-foo

    assert-file-not-exists /tmp/tmp-foo
}

test-file-exists-for-failure() {
    rm /tmp/tmp-foo

    assert-file-exists /tmp/tmp-foo
}

# Will fail
unit-test-should-pass test-file-exists-for-failure

# Will pass
unit-test-should-fail test-file-exists-for-success

unit-test-setup

There are times when one needs work performed before a unit test is run. Simply provide a function entitled unit-test-setup and it will be run before each unit test.

Example
unit-test-setup() {
    export TEMP_DIR=/tmp/${USER}/`date +%N`
    mkdir -p ${TEMP_DIR}
}

This will ensure a temporary directory is created for a user and store the name of the directory created in the variable entitled ${TEMP_DIR}.

unit-test-teardown

There are times when one needs work performed after a unit test is run. Simply provide a function entitled unit-test-teardown and it will be run after each unit test.

Example
unit-test-teardown() {
    rm -rf ${TEMP_DIR}
}

Using the above unit-test-setup() example, after each unit test is run, the created temporary directory will be removed.

Test Suite

A test suite represents a collection of unit tests that should be executed. Simply provide a script containing the function run-test-suite() and pass to the script test-suite.sh.

test-suite-start

Framework functions

The functions defined in this section should not be overridden.

unit-test

This function emits some informational output as a unit test is executing. Additionally, it calls:

  • unit-test-setup: executed before a unit test runs.
  • unit-test-teardown: executed after a unit test runs.

test-suite-start

This function emits an informational message that testing is executing as well as defines global variables for total failed and passed tests. Additionally, it calls:

  • test-suite-setup: executed before a test suite runs.

test-suite-end

This function emits an information message about the number of passed and failed tests. If there are any failures, exits with a status code of 1. Additionally, it calls:

  • test-suite-teardown: executed after a test suite runs.

Covered Code

The following unit tests have high test coverage:

Clone this wiki locally