Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@

### Changed
- `bashunit doc <filter>` says `No assertion matches '<filter>'` instead of printing nothing, which was indistinguishable from a broken install. Mirrors the existing `--custom` wording; the exit code stays 0 because `doc` is informational (#1201)
- Docs: the test-function name rule is stated correctly — the prefix is a literal, lowercase `test_`, so the guide's own `testRenderAllTestsPassedWhenNotFailedTests` example and its claim that names are case-insensitive were both wrong, and a function named either way is silently never run (#1215)
- Docs: `assert_matches` runs `grep -E` in a subprocess per call — ~2.5ms against ~0.065ms for `assert_same`, about 38x — so hot loops matching a fixed substring should prefer `assert_contains`. The subprocess is required by the Bash 3.0 floor (#1187)
- Docs: `assert_match_snapshot` warns that a `@data_provider` test shares one snapshot across all its values — the filename comes from the test function, so the first value creates it and the rest fail against its content. `assert_match_named_snapshot "$1"` gives each value its own (#1185)
- A test file that fails to source without writing to stderr now reports its size and says there was no stderr, so a truncated file can be told apart from one whose last command returned non-zero (#1137)
Expand Down
21 changes: 16 additions & 5 deletions docs/test-files.md
Original file line number Diff line number Diff line change
Expand Up @@ -23,20 +23,31 @@ This is useful regardless of whether your test files are located near your produ
## Test function names

**bashunit** will search for and execute all test functions it finds within each test file.
To distinguish test functions from auxiliary functions, the names must be prefixed with the word `test`.
The function names are case-insensitive.
To distinguish test functions from auxiliary functions, the name must start with `test_` —
lowercase, and the underscore is part of the prefix. Everything after it is yours.
Below are some example test function names that would work seamlessly:

::: code-group
```bash [Example]
function test_should_validate_an_ok_exit_code() { ... }
function testRenderAllTestsPassedWhenNotFailedTests { ... }
test_getFunctionsToRun_with_filter_should_return_matching_functions() { ... }
function test_getFunctionsToRun_with_filter_should_return_matching_functions { ... }
test_render_all_tests_passed_when_not_failed_tests() { ... }
```
:::

::: tip
You're free to use any of Bash's syntax options to define these functions.
You're free to use any of Bash's syntax options to define these functions: `function name()`,
`name()`, or `function name` without parentheses. The syntax is never what decides whether a
function runs — only the name is.
:::

::: warning
`test_` is matched literally, so `testRenderAllTests` (no underscore) and `TEST_upper`
(uppercase) are **auxiliary functions**, not tests. bashunit does not warn about them: if the
file also holds a real test, the run is green and simply contains fewer tests than you wrote.
Check with [`--list`](/command-line#list) when a test you expected never appears in the output.

This is also what lets a helper named `testdata_path` stay a helper.
:::

## Custom test titles
Expand Down
76 changes: 76 additions & 0 deletions tests/acceptance/bashunit_test_function_names_test.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
#!/usr/bin/env bash
set -euo pipefail

# Which functions count as tests is the first thing a user has to get right, and
# getting it wrong is silent: an uncollected function leaves the run green with
# fewer tests than the file defines. docs/test-files.md promised two styles the
# matcher never accepted -- a name with no underscore after `test`, and any
# capitalisation of the prefix (#1215).
#
# The rule `bashunit::helper::get_functions_to_run` implements is a literal,
# lowercase `test_` prefix. Pin it here so the guide and the matcher cannot
# drift apart again in either direction.

function set_up_before_script() {
BASHUNIT_BIN="$(pwd)/bashunit"
}

function set_up() {
WORKDIR="$(bashunit::temp_dir)"
}

function _list() { # $1 = file contents
printf '%s\n' "$1" >"$WORKDIR/n_test.sh"
(cd "$WORKDIR" && "$BASHUNIT_BIN" --no-parallel --list n_test.sh 2>&1) || true
}

# The three definition *styles* are all fine -- `function name()`, `name()` and
# `function name` without parentheses. What decides collection is the name.
function test_every_definition_style_is_collected_when_the_name_starts_with_test_() {
local output
output=$(_list '#!/usr/bin/env bash
function test_with_function_keyword_and_parens() { assert_same 1 1; }
test_with_parens_only() { assert_same 1 1; }
function test_without_parens { assert_same 1 1; }')

assert_contains "test_with_function_keyword_and_parens" "$output"
assert_contains "test_with_parens_only" "$output"
assert_contains "test_without_parens" "$output"
}

# The underscore is part of the prefix, so a camelCase name is a helper.
function test_a_name_without_the_underscore_is_not_collected() {
local output
output=$(_list '#!/usr/bin/env bash
function testRenderAllTestsPassed { assert_same 1 1; }
function test_real_1() { assert_same 1 1; }')

assert_contains "test_real_1" "$output"
assert_not_contains "testRenderAllTestsPassed" "$output"
}

# The prefix is matched case-sensitively.
function test_the_prefix_is_case_sensitive() {
local output
output=$(_list '#!/usr/bin/env bash
function TEST_upper() { assert_same 1 1; }
function Test_mixed() { assert_same 1 1; }
function test_real_2() { assert_same 1 1; }')

assert_contains "test_real_2" "$output"
assert_not_contains "TEST_upper" "$output"
assert_not_contains "Test_mixed" "$output"
}

# A helper whose name merely begins with the letters `test` must stay a helper:
# this is what a bare `test` prefix would break, and why the matcher was left
# alone in favour of correcting the guide.
function test_a_helper_named_like_a_test_is_left_alone() {
local output
output=$(_list '#!/usr/bin/env bash
function testdata_path() { echo /tmp; }
function test_real_3() { assert_same 1 1; }')

assert_contains "test_real_3" "$output"
assert_not_contains "testdata_path" "$output"
}
Loading