diff --git a/docs/CHANGELOG.md b/docs/CHANGELOG.md index b862ab0ddd..75a7140bcc 100644 --- a/docs/CHANGELOG.md +++ b/docs/CHANGELOG.md @@ -11,6 +11,10 @@ The format is based on [Keep a Changelog][kac] and this project adheres to ## [Unreleased] +### Added + +* `bats::on_failure` hook that gets called when a test or `setup*` function fails (#1031) + ## [1.11.1] - 2024-11-29 ### Added diff --git a/lib/bats-core/tracing.bash b/lib/bats-core/tracing.bash index c7cb07319f..0fc12102f8 100644 --- a/lib/bats-core/tracing.bash +++ b/lib/bats-core/tracing.bash @@ -386,8 +386,14 @@ bats_setup_tracing() { trap 'bats_error_trap' ERR } +# predefine to avoid problems when the user does not declare one +bats::on_failure() { + : +} + bats_error_trap() { bats_check_status_from_trap + bats::on_failure "$BATS_ERROR_STATUS" # If necessary, undo the most recent stack trace captured by bats_debug_trap. # See bats_debug_trap for details. diff --git a/test/bats.bats b/test/bats.bats index 1cd1887b17..b2957bc340 100755 --- a/test/bats.bats +++ b/test/bats.bats @@ -1500,4 +1500,50 @@ END_OF_ERR_MSG bats_require_minimum_version 1.5.0 reentrant_run -0 bats --print-output-on-failure "$FIXTURE_ROOT/preserve_IFS" --filter-tags '' +} + +@test "failure callback in test" { + bats_require_minimum_version 1.5.0 + + reentrant_run -1 bats --show-output-of-passing-tests --print-output-on-failure "$FIXTURE_ROOT/failure_callback.bats" + + [ "${lines[0]}" = 1..3 ] + [ "${lines[1]}" = 'not ok 1 failure callback is called on failure' ] + [ "${lines[2]}" = "# (in test file $RELATIVE_FIXTURE_ROOT/failure_callback.bats, line 6)" ] + [ "${lines[3]}" = "# \`false' failed" ] + [ "${lines[4]}" = "# called failure callback" ] + [ "${lines[5]}" = 'ok 2 failure callback is not called on success' ] + [ "${lines[6]}" = '# passed' ] + # this test should not contain: called failure callback + [ "${lines[7]}" = 'not ok 3 failure callback can be overridden locally' ] + [ "${lines[8]}" = "# (in test file $RELATIVE_FIXTURE_ROOT/failure_callback.bats, line 17)" ] + [ "${lines[9]}" = "# \`false' failed" ] + [ "${lines[10]}" = "# override failure callback" ] + [ ${#lines[@]} -eq 11 ] +} + +@test "failure callback in setup_file" { + bats_require_minimum_version 1.5.0 + + reentrant_run -1 bats --print-output-on-failure "$FIXTURE_ROOT/failure_callback_setup_file.bats" + + [ "${lines[0]}" = 1..1 ] + [ "${lines[1]}" = 'not ok 1 setup_file failed' ] + [ "${lines[2]}" = "# (from function \`setup_file' in test file $RELATIVE_FIXTURE_ROOT/failure_callback_setup_file.bats, line 6)" ] + [ "${lines[3]}" = "# \`false' failed" ] + [ "${lines[4]}" = '# failure callback' ] + [ ${#lines[@]} -eq 5 ] +} + +@test "failure callback in setup_suite" { + bats_require_minimum_version 1.5.0 + + reentrant_run -1 bats --print-output-on-failure "$FIXTURE_ROOT/failure_callback_setup_suite" + + [ "${lines[0]}" = 1..1 ] + [ "${lines[1]}" = 'not ok 1 setup_suite' ] + [ "${lines[2]}" = "# (from function \`setup_suite' in test file $RELATIVE_FIXTURE_ROOT/failure_callback_setup_suite/setup_suite.bash, line 6)" ] + [ "${lines[3]}" = "# \`false' failed" ] + [ "${lines[4]}" = '# failure callback' ] + [ ${#lines[@]} -eq 5 ] } \ No newline at end of file diff --git a/test/fixtures/bats/failure_callback.bats b/test/fixtures/bats/failure_callback.bats new file mode 100644 index 0000000000..1934b05a9d --- /dev/null +++ b/test/fixtures/bats/failure_callback.bats @@ -0,0 +1,19 @@ +bats::on_failure() { + # shellcheck disable=SC2317 + echo "called failure callback" +} + +@test failure callback is called on failure { + false +} + +@test failure callback is not called on success { + echo passed +} + +@test failure callback can be overridden locally { + bats::on_failure() { + echo "override failure callback" + } + false +} \ No newline at end of file diff --git a/test/fixtures/bats/failure_callback_setup_file.bats b/test/fixtures/bats/failure_callback_setup_file.bats new file mode 100644 index 0000000000..5454108d70 --- /dev/null +++ b/test/fixtures/bats/failure_callback_setup_file.bats @@ -0,0 +1,11 @@ +bats::on_failure() { + echo "failure callback" +} + +setup_file() { + false +} + +@test dummy { + true +} \ No newline at end of file diff --git a/test/fixtures/bats/failure_callback_setup_suite/dummy.bats b/test/fixtures/bats/failure_callback_setup_suite/dummy.bats new file mode 100644 index 0000000000..5cccbef218 --- /dev/null +++ b/test/fixtures/bats/failure_callback_setup_suite/dummy.bats @@ -0,0 +1,3 @@ +@test dummy { + true +} \ No newline at end of file diff --git a/test/fixtures/bats/failure_callback_setup_suite/setup_suite.bash b/test/fixtures/bats/failure_callback_setup_suite/setup_suite.bash new file mode 100644 index 0000000000..14b1ecde6d --- /dev/null +++ b/test/fixtures/bats/failure_callback_setup_suite/setup_suite.bash @@ -0,0 +1,7 @@ +bats::on_failure() { + echo "failure callback" +} + +setup_suite() { + false +} \ No newline at end of file