Skip to content

feat(doubles): add an any-call matcher for spy arguments #897

Description

@Chemaclass

Context

assert_have_been_called_with <spy> <expected> checks the last recorded call
(tail -n 1, src/test_doubles.sh:159). There is no way to assert "this spy was
called with these arguments at some point", which is the common intent when the
command under test invokes the same helper several times.

The workaround is to know the exact index and use
assert_have_been_called_with <spy> <expected> <n> or
assert_have_been_called_nth_with <n> <spy> <expected> — which couples the test
to call ordering that is usually irrelevant.

Evidence

function test_called_with_only_checks_last_call() {
  bashunit::spy touch
  touch first
  touch second
  assert_have_been_called_with touch "first"   # FAILS: Expected 'first' but got 'second'
}

Nothing in the message says "checked the last call only", so the failure reads
like the call never happened.

Proposal

Add an any-call assertion, e.g.
assert_have_been_called_with_any <spy> <expected> (name open to discussion),
that scans every recorded line and passes if any matches.

Two smaller alternatives, if a new assertion is judged too much surface:

  • accept a literal any in the index position:
    assert_have_been_called_with touch "first" any
  • keep the assertion count as-is and only fix the message to state which call was
    compared (but got 'second' (last of 2 calls))

Recommendation: ship the new assertion and the clearer message. The message
fix is worth having regardless of which matcher the user picked.

Why this matters for agentic coding

Order-coupled assertions are a maintenance tax an agent pays repeatedly: it adds
a log line to a function, an unrelated assert_have_been_called_with breaks, and
the failure text points at neither the cause nor the fix. An any-call matcher lets
an agent write the assertion that expresses the actual requirement, which is
almost always "this side effect happened", not "this side effect happened last".

Acceptance criteria

  • Any-call matcher passes when a non-last call matches
  • Existing last-call and indexed behaviour unchanged
  • Failure message of assert_have_been_called_with states which call it
    compared
  • Tests in tests/functional/doubles_test.sh for match-on-first,
    match-on-middle, and no-match
  • Documented in docs/test-doubles.md, docs/public/bashunit-skill.md and
    docs/ai-agents.md (the argument-order table)
  • Bash 3.0+ compatible, make sa + make lint green

Metadata

Metadata

Assignees

Labels

Type

No type

Projects

Status
Done

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions