Skip to content

Commit 73d0718

Browse files
committed
fix: test doubles when running tests in parallel by isolating spy files per test
1 parent 501b074 commit 73d0718

4 files changed

Lines changed: 42 additions & 44 deletions

File tree

CHANGELOG.md

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,6 @@
33
## Unreleased
44

55
- Fix asserts on test doubles in subshell
6-
- Fix test doubles when running tests in parallel
76
- Allow interpolating arguments in data providers output
87

98
## [0.19.1](https://github.com/TypedDevs/bashunit/compare/0.19.0...0.19.1) - 2025-05-23

docs/test-doubles.md

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,9 @@ When creating tests, you might need to override existing function to be able to
44

55
Temporary files created by spies are isolated per test run, so they work reliably when executing tests in parallel.
66

7+
Spies record their calls in temporary files scoped to each test run.
8+
This avoids clashes between processes and allows spies to work reliably when tests execute in parallel using `BASHUNIT_PARALLEL_RUN`.
9+
710
## mock
811
> `mock "function" "body"`
912

src/runner.sh

Lines changed: 35 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -141,7 +141,11 @@ function runner::run_test() {
141141
# race conditions when running tests in parallel.
142142
local sanitized_fn_name
143143
sanitized_fn_name="$(helper::normalize_variable_name "$fn_name")"
144-
export BASHUNIT_CURRENT_TEST_ID="${sanitized_fn_name}_$$_$(random_str 6)"
144+
if env::is_parallel_run_enabled; then
145+
export BASHUNIT_CURRENT_TEST_ID="${sanitized_fn_name}_$$_$(random_str 6)"
146+
else
147+
export BASHUNIT_CURRENT_TEST_ID="${sanitized_fn_name}_$$"
148+
fi
145149

146150
local interpolated_fn_name="$(helper::interpolate_function_name "$fn_name" "$@")"
147151
local current_assertions_failed="$(state::get_assertions_failed)"
@@ -344,40 +348,45 @@ function runner::parse_result_parallel() {
344348
}
345349

346350
# shellcheck disable=SC2295
347-
function runner::_extract_field_value() {
348-
local line=$1
349-
local key=$2
350-
351-
local value="${line##*##${key}=}"
352-
value="${value%%##*}"
353-
354-
echo "$value"
355-
}
356-
357351
function runner::parse_result_sync() {
358352
local fn_name=$1
359353
local execution_result=$2
360354

361-
local trimmed="${execution_result%$'\n'}"
362-
local result_line="${trimmed##*$'\n'}"
363-
364-
local assertions_failed
365-
assertions_failed=$(runner::_extract_field_value "$result_line" "ASSERTIONS_FAILED")
355+
local assertions_failed=$(\
356+
echo "$execution_result" |\
357+
tail -n 1 |\
358+
sed -E -e 's/.*##ASSERTIONS_FAILED=([0-9]*)##.*/\1/g'\
359+
)
366360

367-
local assertions_passed
368-
assertions_passed=$(runner::_extract_field_value "$result_line" "ASSERTIONS_PASSED")
361+
local assertions_passed=$(\
362+
echo "$execution_result" |\
363+
tail -n 1 |\
364+
sed -E -e 's/.*##ASSERTIONS_PASSED=([0-9]*)##.*/\1/g'\
365+
)
369366

370-
local assertions_skipped
371-
assertions_skipped=$(runner::_extract_field_value "$result_line" "ASSERTIONS_SKIPPED")
367+
local assertions_skipped=$(\
368+
echo "$execution_result" |\
369+
tail -n 1 |\
370+
sed -E -e 's/.*##ASSERTIONS_SKIPPED=([0-9]*)##.*/\1/g'\
371+
)
372372

373-
local assertions_incomplete
374-
assertions_incomplete=$(runner::_extract_field_value "$result_line" "ASSERTIONS_INCOMPLETE")
373+
local assertions_incomplete=$(\
374+
echo "$execution_result" |\
375+
tail -n 1 |\
376+
sed -E -e 's/.*##ASSERTIONS_INCOMPLETE=([0-9]*)##.*/\1/g'\
377+
)
375378

376-
local assertions_snapshot
377-
assertions_snapshot=$(runner::_extract_field_value "$result_line" "ASSERTIONS_SNAPSHOT")
379+
local assertions_snapshot=$(\
380+
echo "$execution_result" |\
381+
tail -n 1 |\
382+
sed -E -e 's/.*##ASSERTIONS_SNAPSHOT=([0-9]*)##.*/\1/g'\
383+
)
378384

379-
local test_exit_code
380-
test_exit_code=$(runner::_extract_field_value "$result_line" "TEST_EXIT_CODE")
385+
local test_exit_code=$(\
386+
echo "$execution_result" |\
387+
tail -n 1 |\
388+
sed -E -e 's/.*##TEST_EXIT_CODE=([0-9]*)##.*/\1/g'\
389+
)
381390

382391
log "debug" "[SYNC]" "fn_name:$fn_name" "execution_result:$execution_result"
383392

src/test_doubles.sh

Lines changed: 4 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -15,8 +15,6 @@ function unmock() {
1515
local params_file_var="${variable}_params_file"
1616
[[ -f "${!times_file_var-}" ]] && rm -f "${!times_file_var}"
1717
[[ -f "${!params_file_var-}" ]] && rm -f "${!params_file_var}"
18-
unset "${variable}_times"
19-
unset "${variable}_params"
2018
unset "$times_file_var"
2119
unset "$params_file_var"
2220
break
@@ -44,9 +42,6 @@ function spy() {
4442
local variable
4543
variable="$(helper::normalize_variable_name "$command")"
4644

47-
export "${variable}_times"=0
48-
export "${variable}_params"
49-
5045
local times_file params_file
5146
local test_id="${BASHUNIT_CURRENT_TEST_ID:-global}"
5247
times_file=$(temp_file "${test_id}_${variable}_times")
@@ -57,9 +52,7 @@ function spy() {
5752
export "${variable}_params_file"="$params_file"
5853

5954
eval "function $command() {
60-
${variable}_params=(\"\$*\")
6155
echo \"\$*\" > '$params_file'
62-
((${variable}_times++)) || true
6356
local _c=\$(cat '$times_file')
6457
_c=\$((_c+1))
6558
echo \"\$_c\" > '$times_file'
@@ -74,10 +67,8 @@ function assert_have_been_called() {
7467
local command=$1
7568
local variable
7669
variable="$(helper::normalize_variable_name "$command")"
77-
local actual
78-
actual="${variable}_times"
7970
local file_var="${variable}_times_file"
80-
local times="${!actual-0}"
71+
local times=0
8172
if [[ -f "${!file_var-}" ]]; then
8273
times=$(cat "${!file_var}")
8374
fi
@@ -97,10 +88,8 @@ function assert_have_been_called_with() {
9788
local command=$2
9889
local variable
9990
variable="$(helper::normalize_variable_name "$command")"
100-
local actual
101-
actual="${variable}_params"
10291
local file_var="${variable}_params_file"
103-
local params="${!actual-}"
92+
local params=""
10493
if [[ -f "${!file_var-}" ]]; then
10594
params=$(cat "${!file_var}")
10695
fi
@@ -120,15 +109,13 @@ function assert_have_been_called_times() {
120109
local command=$2
121110
local variable
122111
variable="$(helper::normalize_variable_name "$command")"
123-
local actual
124-
actual="${variable}_times"
125112
local file_var="${variable}_times_file"
126-
local times="${!actual-0}"
113+
local times=0
127114
if [[ -f "${!file_var-}" ]]; then
128115
times=$(cat "${!file_var}")
129116
fi
130117
local label="${3:-$(helper::normalize_test_function_name "${FUNCNAME[1]}")}"
131-
if [[ -z "${!actual-}" && $expected -ne 0 || $times -ne $expected ]]; then
118+
if [[ $times -ne $expected ]]; then
132119
state::add_assertions_failed
133120
console_results::print_failed_test "${label}" "${command}" \
134121
"to has been called" "${expected} times" \

0 commit comments

Comments
 (0)