Skip to content

Custom asserts: a supported way to test that an assertion fails #916

Description

@Chemaclass

Background

There is no supported way to test that a custom assertion fails the way you intended. To check its failure output you have to reconstruct the expected string from an internal renderer.

bashunit's own suite does exactly that, in tests/functional/custom_asserts_test.sh:

function test_assert_foo_failed() {
  assert_same "$(bashunit::console_results::print_failed_test "Assert foo failed" "foo" "but got " "bar")" \
    "$(assert_foo "bar")"
}

tests/unit/custom_assertions_test.sh goes further and monkey-patches bashunit::console_results::print_line inside a subshell to capture output.

If the framework has to break its own encapsulation to test a custom assertion, a user has no chance. It also means any change to the failure renderer breaks those tests for reasons unrelated to the assertion under test.

Proposal

Two assertions for asserting about assertions:

assert_assertion_fails <cmd> [args...]
assert_assertion_passes <cmd> [args...]

Each one runs the command with the assertion counters snapshotted, restores them afterwards so the inner assertion does not pollute the test's own totals, and reports a single assertion of its own.

function test_assert_positive_number_rejects_zero() {
  assert_assertion_fails assert_positive_number 0
}

function test_assert_positive_number_accepts_one() {
  assert_assertion_passes assert_positive_number 1
}

An optional message check keeps the output contract testable without touching console_results:

assert_assertion_fails_with "positive number" assert_positive_number 0

Notes

  • The counter snapshot and restore is the load-bearing part. Without it, the inner failure leaves _BASHUNIT_ASSERTION_FAILED_IN_TEST set, and every later assertion in the same test is skipped when stop-on-assertion-failure is on (src/assert.sh:10).
  • Once this exists, tests/functional/custom_asserts_test.sh and tests/unit/custom_assertions_test.sh should be rewritten on top of it, which is the real proof that the API is good enough.

Part of a custom-asserts DX set, each sized as its own PR:

Metadata

Metadata

Assignees

No one assigned

    Labels

    Type

    No type

    Projects

    Status
    No status

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions