diff --git a/CHANGELOG.md b/CHANGELOG.md index da594043..6d58f0c2 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,7 +2,7 @@ ## [Unreleased](https://github.com/TypedDevs/bashunit/compare/0.12.0...main) -- ... +- Allow calling assertions standalone outside tests ## [0.12.0](https://github.com/TypedDevs/bashunit/compare/0.11.0...0.12.0) - 2024-06-11 diff --git a/bashunit b/bashunit index 99cef415..34f29207 100755 --- a/bashunit +++ b/bashunit @@ -17,17 +17,20 @@ source "$BASHUNIT_ROOT_DIR/src/helpers.sh" source "$BASHUNIT_ROOT_DIR/src/upgrade.sh" source "$BASHUNIT_ROOT_DIR/src/assertions.sh" source "$BASHUNIT_ROOT_DIR/src/runner.sh" +source "$BASHUNIT_ROOT_DIR/src/main.sh" -############### -#### MAIN ##### -############### - +_ASSERT_FN="" _FILTER="" -_FILES=() +_ARGS=() while [[ $# -gt 0 ]]; do argument="$1" case $argument in + -a|--assert) + _ASSERT_FN="$2" + shift + shift + ;; -f|--filter) _FILTER="$2" shift @@ -65,15 +68,15 @@ while [[ $# -gt 0 ]]; do ;; *) while IFS='' read -r line; do - _FILES+=("$line"); + _ARGS+=("$line"); done < <(helper::find_files_recursive "$argument") shift ;; esac done -console_header::print_version_with_env -runner::load_test_files "$_FILTER" "${_FILES[@]}" -console_results::render_result - -exit 0 +if [[ -n "$_ASSERT_FN" ]]; then + main::exec_assert "$_ASSERT_FN" "${_ARGS[@]}" +else + main::exec_tests "$_FILTER" "${_ARGS[@]}" +fi diff --git a/docs/.vitepress/config.ts b/docs/.vitepress/config.ts index 63e3c3a4..a75c583d 100644 --- a/docs/.vitepress/config.ts +++ b/docs/.vitepress/config.ts @@ -72,6 +72,9 @@ export default defineConfig({ }, { text: 'Skipping/incomplete', link: '/skipping-incomplete' + }, { + text: 'Standalone', + link: '/standalone' }, { text: 'Examples', link: '/examples' diff --git a/docs/command-line.md b/docs/command-line.md index ab17690e..a9a5d9e8 100644 --- a/docs/command-line.md +++ b/docs/command-line.md @@ -28,6 +28,35 @@ to choose where the tests are located by default. ``` ::: +## Assert + +> `bashunit -a|--assert function "arg1" "arg2"` + +Run a core assert function standalone without a test context. Read more: [Standalone](/standalone) + +::: code-group +```bash [Example] +./bashunit --assert equals "foo" "bar" +``` +```bash [Output] +✗ Failed: Main::exec assert + Expected 'foo' + but got 'bar' +``` +::: + +## Environment + +> `bashunit -e|--env "file path"` + +Load a custom env file overriding the `.env` environment variables. + +::: code-group +```bash [Example] +./bashunit tests --env .env.production +``` +::: + ## Filter > `bashunit -f|--filter "test name"` @@ -97,18 +126,6 @@ to make this behavior permanent. ``` ::: -## Environment - -> `bashunit --env "file path"` - -Load a custom env file overriding the `.env` environment variables. - -::: code-group -```bash [Example] -./bashunit tests --env .env.production -``` -::: - ## Version > `bashunit --version` diff --git a/docs/standalone.md b/docs/standalone.md new file mode 100644 index 00000000..ea3bd2a7 --- /dev/null +++ b/docs/standalone.md @@ -0,0 +1,40 @@ +# Standalone + +You can use all bashunit assertions outside any tests if you would like to use them in integration or end-to-end tests, for entire applications or executables. + +## Executing an assertion + +If you want to use the nice assertions syntax of bashunit - without the tests context/functions, but to end-to-end tests executables, you can use the `-a|--assert` option when running `./bashunit` and call the assertion directly from there. + +The return exit code will be `0` for success assertions and `1` for failures. + +::: info +The prefix `assert_` is optional. +::: + +::: code-group +```bash [Example] +# with `assert_` prefix +./bashunit -a assert_equals "foo" "foo" +# or without prefix +./bashunit -a equals "foo" "foo" +``` +```[Output] +# No output - exit code 0 (success) +``` +::: + +::: code-group +```bash [Example] +# with `assert_` prefix +./bashunit -a assert_not_equals "foo" "foo" +# or without prefix +./bashunit -a not_equals "foo" "foo" +``` +```[Output] +✗ Failed: Main::exec not_equals + Expected 'foo' + but got 'foo' +``` +::: + diff --git a/src/console_header.sh b/src/console_header.sh index 3d413cf5..17d277de 100644 --- a/src/console_header.sh +++ b/src/console_header.sh @@ -33,6 +33,12 @@ Arguments: If you use wildcards, bashunit will run any tests it finds. Options: + -a|--assert + Run a core assert function standalone without a test context. + + -e|--env + Load a custom env file overriding the .env environment variables. + -f|--filter Filters the tests to run based on the test name. @@ -42,9 +48,6 @@ Options: -S|--stop-on-failure Force to stop the runner right after encountering one failing test. - -e|--env - Load a custom env file overriding the .env environment variables. - --version Displays the current version of bashunit. diff --git a/src/main.sh b/src/main.sh new file mode 100644 index 00000000..326a22f7 --- /dev/null +++ b/src/main.sh @@ -0,0 +1,32 @@ +#!/bin/bash + +function main::exec_tests() { + local filter=$1 + local files=("${@:2}") + + console_header::print_version_with_env + runner::load_test_files "$filter" "${files[@]}" + console_results::render_result + exit 0 +} + +function main::exec_assert() { + local original_assert_fn=$1 + local assert_fn=$original_assert_fn + local args=("${@:2}") + + if ! type "$assert_fn" > /dev/null 2>&1; then + # try again using prefix `assert_` + assert_fn="assert_$assert_fn" + if ! type "$assert_fn" > /dev/null 2>&1; then + echo "Function $original_assert_fn does not exist." + exit 1 + fi + fi + + "$assert_fn" "${args[@]}" + + if [[ "$(state::get_tests_failed)" -gt 0 ]] || [[ "$(state::get_assertions_failed)" -gt 0 ]]; then + exit 1 + fi +} diff --git a/src/runner.sh b/src/runner.sh index d1df81c6..930838e5 100755 --- a/src/runner.sh +++ b/src/runner.sh @@ -38,8 +38,7 @@ function runner::functions_for_script() { local script="$1" local all_function_names="$2" - # Filter the names down to the ones defined in the script, sort them by line - # number + # Filter the names down to the ones defined in the script, sort them by line number shopt -s extdebug for f in $all_function_names; do declare -F "$f" | grep "$script" diff --git a/tests/acceptance/bashunit_direct_fn_call_test.sh b/tests/acceptance/bashunit_direct_fn_call_test.sh new file mode 100644 index 00000000..744da99e --- /dev/null +++ b/tests/acceptance/bashunit_direct_fn_call_test.sh @@ -0,0 +1,34 @@ +#!/bin/bash + +function set_up_before_script() { + TEST_ENV_FILE="tests/acceptance/fixtures/.env.default" +} + +function test_bashunit_direct_fn_call_passes() { + local expected="foo" + local actual="foo" + + ./bashunit -a assert_equals --env "$TEST_ENV_FILE" "$expected" $actual + assert_successful_code +} + +function test_bashunit_direct_fn_call_without_assert_prefix_passes() { + local expected="foo" + local actual="foo" + + ./bashunit -a equals --env "$TEST_ENV_FILE" "$expected" $actual + assert_successful_code +} + +function test_bashunit_direct_fn_call_failure() { + local expected="foo" + local actual="bar" + + assert_match_snapshot "$(./bashunit -a assert_equals --env "$TEST_ENV_FILE" "$expected" $actual)" + assert_general_error "$(./bashunit -a assert_equals --env "$TEST_ENV_FILE" "$expected" $actual)" +} + +function test_bashunit_direct_fn_call_non_existing_fn() { + assert_match_snapshot "$(./bashunit -a non_existing_fn --env "$TEST_ENV_FILE")" + assert_general_error "$(./bashunit -a non_existing_fn --env "$TEST_ENV_FILE")" +} diff --git a/tests/acceptance/snapshots/bashunit_direct_fn_call_test_sh.test_bashunit_direct_fn_call_failure.snapshot b/tests/acceptance/snapshots/bashunit_direct_fn_call_test_sh.test_bashunit_direct_fn_call_failure.snapshot new file mode 100644 index 00000000..bbbd30d6 --- /dev/null +++ b/tests/acceptance/snapshots/bashunit_direct_fn_call_test_sh.test_bashunit_direct_fn_call_failure.snapshot @@ -0,0 +1,3 @@ +✗ Failed: Main::exec assert + Expected 'foo' + but got 'bar' diff --git a/tests/acceptance/snapshots/bashunit_direct_fn_call_test_sh.test_bashunit_direct_fn_call_non_existing_fn.snapshot b/tests/acceptance/snapshots/bashunit_direct_fn_call_test_sh.test_bashunit_direct_fn_call_non_existing_fn.snapshot new file mode 100644 index 00000000..321ac66b --- /dev/null +++ b/tests/acceptance/snapshots/bashunit_direct_fn_call_test_sh.test_bashunit_direct_fn_call_non_existing_fn.snapshot @@ -0,0 +1 @@ +Function non_existing_fn does not exist. diff --git a/tests/acceptance/snapshots/bashunit_path_test_sh.test_bashunit_without_path_env_nor_argument.snapshot b/tests/acceptance/snapshots/bashunit_path_test_sh.test_bashunit_without_path_env_nor_argument.snapshot index c8d0b7d1..9279b887 100644 --- a/tests/acceptance/snapshots/bashunit_path_test_sh.test_bashunit_without_path_env_nor_argument.snapshot +++ b/tests/acceptance/snapshots/bashunit_path_test_sh.test_bashunit_without_path_env_nor_argument.snapshot @@ -7,6 +7,12 @@ Arguments: If you use wildcards, bashunit will run any tests it finds. Options: + -a|--assert + Run a core assert function standalone without a test context. + + -e|--env + Load a custom env file overriding the .env environment variables. + -f|--filter Filters the tests to run based on the test name. @@ -16,9 +22,6 @@ Options: -S|--stop-on-failure Force to stop the runner right after encountering one failing test. - -e|--env - Load a custom env file overriding the .env environment variables. - --version Displays the current version of bashunit.