This document provides a comprehensive overview of the bashunit testing framework, including its architecture, core subsystems, and execution model. This page covers the high-level organization of the codebase and how components interact. For detailed information about specific subsystems, see:
bashunit is a testing framework for Bash scripts that provides comprehensive testing capabilities including assertions, test doubles (mocks and spies), lifecycle hooks, parallel execution, code coverage analysis, and multiple output formats. The framework is distributed as a single self-contained executable file (bashunit) that requires Bash version 3.2 or higher.
The framework follows a familiar testing pattern similar to PHPUnit, Jest, and other modern testing frameworks, making it accessible to developers with experience in other languages. Tests are written as Bash functions prefixed with test_ in files suffixed with _test.sh or _Test.sh.
Sources: bashunit1-31 README.md35-46 CHANGELOG.md1-50
bashunit is organized into seven logical layers, each responsible for a distinct aspect of the testing framework:
Architecture Layers
| Layer | Primary Files | Responsibility |
|---|---|---|
| Entry Point | bashunit | Main executable, version checking, subcommand routing |
| Command Dispatch | src/main.sh | Parses CLI arguments, routes to appropriate command handlers |
| Core Execution | src/runner.sh, src/bashunit.sh, src/parallel.sh | Discovers tests, orchestrates execution, manages parallel processes |
| Testing Features | src/assertions.sh, test doubles in assertions | Provides assertion functions and test double mechanisms |
| State & Output | src/state.sh, src/console_results.sh, src/reports.sh | Tracks test results, formats output, generates reports |
| Infrastructure | src/env.sh, src/helpers.sh, src/clock.sh, etc. | Environment configuration, utilities, OS detection, timing |
| Distribution | install.sh, build.sh | Installation script, build system for single-file distribution |
Sources: bashunit57-82 CHANGELOG.md71-75
The bashunit executable serves as the sole entry point for all operations. It implements a subcommand-based CLI architecture introduced in version 0.28.0:
Command Routing Logic
The main executable performs early initialization before dispatching to command handlers:
--skip-env-file, --login, --no-color before loading environment bashunit42-55The default subcommand is test, which allows backward compatibility with the original syntax where bashunit [path] directly runs tests.
Sources: bashunit1-127 CHANGELOG.md93-103
The test execution pipeline follows a consistent flow regardless of whether tests run sequentially or in parallel:
Key Execution Stages
| Stage | Module | Functions | Description |
|---|---|---|---|
| Configuration Loading | src/env.sh | bashunit::env::load | Loads .env file and applies environment variables |
| Bootstrap Loading | src/runner.sh | bashunit::runner::load_bootstrap | Sources optional bootstrap script for custom setup |
| Test Discovery | src/helpers.sh | bashunit::helper::find_files_recursivebashunit::helper::get_functions_to_run | Finds test files matching patterns and extracts test functions |
| Execution Mode | src/parallel.sh or src/runner.sh | bashunit::parallel::run_test_filesbashunit::runner::run_test_files | Executes tests sequentially or in parallel |
| Lifecycle Hooks | User test files | set_up_before_scriptset_uptear_downtear_down_after_script | Provides setup and cleanup at file and test level |
| Test Execution | src/runner.sh | bashunit::runner::call_test | Runs individual test functions in isolated subshells |
| State Management | src/state.sh | bashunit::state::add_* | Tracks passed/failed/skipped assertions and tests |
| Output Rendering | src/console_results.sh | bashunit::console_results::render_result | Formats test results for console display |
| Report Generation | src/reports.sh | bashunit::reports::generate_report_xmlbashunit::reports::generate_report_html | Generates JUnit XML and HTML reports |
| Coverage Analysis | src/coverage.sh | bashunit::coverage::generate_report | Tracks code coverage using DEBUG trap, generates LCOV and HTML reports |
Sources: bashunit119 CHANGELOG.md17-33
The assertion system provides over 50 assertion functions organized into categories:
| Category | Example Functions | Module |
|---|---|---|
| Value Assertions | assert_equals, assert_not_equals, assert_same, assert_not_same | src/assertions.sh |
| String Assertions | assert_contains, assert_matches, assert_string_starts_with, assert_string_ends_with | src/assertions.sh |
| Command Assertions | assert_exec, assert_exit_code, assert_successful_code, assert_unsuccessful_code | src/assertions.sh |
| File Assertions | assert_file_exists, assert_file_contains, assert_files_equals, assert_is_file_empty | src/assertions.sh |
| Directory Assertions | assert_directory_exists, assert_is_directory_readable, assert_is_directory_writable | src/assertions.sh |
| Array Assertions | assert_array_contains, assert_array_not_contains | src/assertions.sh |
| Snapshot Testing | assert_match_snapshot, assert_match_snapshot_ignore_colors | src/assertions.sh |
| Test Double Assertions | assert_have_been_called, assert_have_been_called_with, assert_have_been_called_times, assert_not_called | src/assertions.sh |
All assertion functions follow a consistent pattern: they call bashunit::state::add_assertions_passed or bashunit::state::add_assertions_failed to update test state, and return appropriate exit codes (0 for pass, 1 for fail).
Since version 0.28.0, tests stop at the first assertion failure within a test function by default, matching PHPUnit and Jest behavior. This can be overridden with the --run-all flag.
Sources: CHANGELOG.md71-76 CHANGELOG.md100-103
Test doubles (mocks and spies) are implemented through temporary file-based function replacement:
bashunit::mock - Replaces a function with a stub that returns a specified valuebashunit::spy - Wraps a function to track its invocations while preserving original behaviorBoth mechanisms create temporary files in $_BASHUNIT_TEMP_DIR to store invocation data, which is automatically cleaned up after each test. The spy system records all arguments passed to each invocation, enabling assertions like assert_have_been_called_with.
Sources: CHANGELOG.md429-433
The src/state.sh module maintains global counters for test execution state:
_BASHUNIT_TESTS_PASSED / _BASHUNIT_TESTS_FAILED / _BASHUNIT_TESTS_SKIPPED / _BASHUNIT_TESTS_INCOMPLETE_BASHUNIT_ASSERTIONS_PASSED / _BASHUNIT_ASSERTIONS_FAILED / _BASHUNIT_ASSERTIONS_SNAPSHOT_BASHUNIT_TOTAL_TESTS / _BASHUNIT_TOTAL_ASSERTIONSState management includes special handling for parallel execution, where tests run in separate subshells. The state is serialized using special markers (##ASSERTIONS_PASSED##, etc.) that the parent process parses to aggregate results.
Sources: CHANGELOG.md71-75
Configuration is loaded through a three-tier hierarchy (highest priority first):
--parallel, --stop-on-failureBASHUNIT_* variables set in the shell.env file - Project-level configuration file (can be skipped with --skip-env-file)The src/env.sh module handles loading and applying configuration, with all settings prefixed with BASHUNIT_ to prevent collisions. Common configuration options include:
BASHUNIT_PARALLEL_RUN - Enable parallel test executionBASHUNIT_SIMPLE_OUTPUT - Use simple output formatBASHUNIT_STOP_ON_FAILURE - Stop after first test failureBASHUNIT_DEFAULT_PATH - Default path for test discoveryBASHUNIT_BOOTSTRAP - Path to bootstrap scriptSources: bashunit42-55 CHANGELOG.md49-50 CHANGELOG.md324-332
bashunit uses a unique single-file distribution model:
Build Process
The build.sh script performs several transformations:
source statements and inlines module contents into a single filedoc commandThis produces a self-contained executable that requires no additional files or dependencies beyond Bash itself.
Installation Methods
| Method | Command | Target Version |
|---|---|---|
| Latest Stable | curl -s https://bashunit.typeddevs.com/install.sh | bash | Most recent release |
| Specific Version | bash <(curl -s https://bashunit.typeddevs.com/install.sh) 0.31.0 | Specified version |
| Beta (Unstable) | bash <(curl -s https://bashunit.typeddevs.com/install.sh) beta | Latest main branch |
The beta installation requires Git and clones the repository to build the latest development version, embedding the commit hash in the version string.
Sources: install.sh1-100 build.sh9-19 CHANGELOG.md335-340 .github/RELEASE.md1-20
bashunit requires Bash 3.2 or newer. The version check is performed immediately upon execution bashunit4-28 ensuring compatibility before any framework code runs.
The current stable version is 0.31.0 bashunit31 released on 2025-12-19, which introduced code coverage tracking via DEBUG trap mechanism.
Major breaking changes occurred in version 0.29.0, which introduced namespace prefixing for all internal functions and variables to prevent collisions with user code. All public assertion functions (assert_*) remain unchanged.
Sources: bashunit4-31 README.md44-46 CHANGELOG.md17-83
Refresh this wiki
This wiki was recently refreshed. Please wait 1 day to refresh again.