Home

Awesome

critic.sh

CI


Dead simple testing framework for Bash with coverage.

asciicast

git-cloc

Why?

I was looking for a Bash testing framework with a familiar API and with coverage reporting. Although there are excellent frameworks like bats-core, shunit2 and bashunit, I wasn't too comfortable with their API (not their fault). Also, I wanted some indication of coverage, so that it can be improved over time.

critic.sh exposes high level functions for testing consistent with other frameworks and a set of built in assertions. One of my most important goals was to be able to pass in any shell expression to the _test and _assert methods, so that one is not limited to the built-ins.

In addition, it can generate a lcov report. It tracks line and function coverage, but not branches. It works by running the tests with extended debugging, redirecting the trace output to a log file, and then parsing it to determine which functions/lines have been executed. It is currently a work in progress.

Requirements

Due to use of certain bashisms, Bash v4.1+ is required. This may change in the future.

A tiny docker image is provided for convenience.

Installation

There are a few ways to use critic.sh:

docker run --rm -v $(pwd):/work checksum/critic.sh '/work/src/*.sh' '/work/lib/*.sh'

You can pass a CRITIC_SETUP environment variable to run setup scripts before the tests are run. The docker image is based on alpine linux, so use apk to install packages:

docker run --rm -e CRITIC_SETUP='apk add --no-cache jq' -v $(pwd):/work checksum/critic.sh '/work/src/*.sh' '/work/lib/*.sh'
git submodule add https://github.com/Checksum/critic.sh critic
critic/critic.sh test.sh

Usage

See examples/test.sh for detailed usage. To run the tests: bash examples/test.sh

Source the framework in your test file

# test-foobar.sh

# Include your source files
source foobar.sh
# Include the framework
source critic.sh

# Write tests
_describe foo
  _test "output should equal foo"
    _assert _output_equals "foo"

  _test "return code should be 0"
    _assert _return_true "Optional assertion message"

Pass the test file as an argument

critic.sh test-foobar.sh

API

The layout of a test is consistent with other frameworks. You _describe a test suite, _test a function or expression, and _assert the output with a function or expression. The output, return code and arguments passed to the test are available as variables for all custom assertions.

Test suite

FunctionDescriptionArguments
_describeRun test suite1. Suite/Function name (*)
_describe_skipSkip this test suite1. Suite/Function name (*)
_testRun a test1. Test name (*)
2. Test function/expression
3. Arguments to forward to the test function
_test_skipSkip this test1. Test name (*)
2. Test function/expression
3. Arguments to forward to the test function
_assertRun an assertion1. Assertion function/expression (*)
2. Arguments to forward to the assertion function
_teardownTeardown function run after all tests have ben run

Assertions

FunctionDescriptionArguments
_return_trueReturn code == 01. Optional message
_return_falseReturn code != 01. Optional message
_return_equalsReturn code == num1. Return code (*)
2. Optional message
_output_containsOutput contains value1. Value (*)
2. Optional message
_output_equalsOutput equals value1. Value (*)
2. Optional message
_notNegate an assertion1. Assertion (*)
2. Value (*)
3. Optional message
_nth_arg_equalsNth arg equals value1. Argument index (>=0)
2. Value
3. Optional message

Variables

After every _test is run, the following variables are set. These are useful for custom assertions:

VariableDescription
_outputOutput of the function/expression
_returnReturn code
_argsArguments passed to the function/expression

Options

Environment variableDescriptionDefault
CRITIC_COVERAGE_DISABLEDisable coveragefalse
CRITIC_COVERAGE_MIN_PERCENTMinimum coverage percent per source file0
CRITIC_COVERAGE_REPORT_CLIPrint coverage report to CLItrue
CRITIC_COVERAGE_REPORT_LCOVSave lcov reporttrue
CRITIC_COVERAGE_REPORT_HTMLGenerate HTML lcov report (requires lcov)false
CRITIC_DEBUGPrints more verbose messagesfalse

Annotations

Disable coverage for certain lines by wrapping them in # critic ignore and # critic /ignore blocks:
# critic ignore
foo() {
  echo "This function will skipped when calculating coverage %"
}
# critic /ignore