Skip to content

Writing expectations ​

Every mocked method exposes an .expect.<method> handle with three branches:

BranchMeaning
.called.*At-least-one assertions — "at least one call matched"
.everyCall.*All-call assertions — "every recorded call matched" (throws on zero calls — no vacuous-true)
.invocation(i)Per-call assertions — "the i-th call matched"

All assertions throw on mismatch with a message that includes the full recorded call history. Framework-agnostic: any test runner that catches errors will report them as failures.

Call counts ​

typescript
mock.expect.greet.called.times(2)
mock.expect.greet.called.once()
mock.expect.greet.called.twice()
mock.expect.greet.called.never()

Comparisons ​

typescript
mock.expect.greet.called.lt(5)
mock.expect.greet.called.lte(4)
mock.expect.greet.called.gt(0)
mock.expect.greet.called.gte(1)

Argument matching ​

withArg(arg) — any invocation had this arg ​

Partial deep match, matcher-aware:

typescript
mock.greet('alice', { name: 'bob', a: 1 })
mock.expect.greet.called.withArg({ name: 'bob' })     // partial object match
mock.expect.greet.called.withArg(match.string)         // matcher

withArgs(...args) — all these args in one call ​

Every listed arg must appear in the same recorded call:

typescript
mock.greet('alice', 'bob')
mock.expect.greet.called.withArgs('alice', 'bob')

Searches strings and nested object values for a regex match:

typescript
mock.greet('The quick brown fox')
mock.expect.greet.called.withMatch(/quick.*fox/)

mock.greet({ message: 'hello world' })
mock.expect.greet.called.withMatch(/hello/)

matchExactly(...args) — strict deep equal, matcher-aware ​

Unlike withArgs, this enforces exact arg list (same length, same order, deep-equal at every position):

typescript
mock.greet('alice', ['carol'], 123)
mock.expect.greet.called.matchExactly('alice', ['carol'], 123)
mock.expect.greet.called.matchExactly(match.string, match.array, match.number)

Return / this / throw ​

withReturn(expected) ​

Assert at least one call returned expected. Works with values or matchers; for async methods the resolved value is NOT awaited — you're asserting against the literal return, which is the Promise:

typescript
mock.setup.sum.toReturn(42)
mock.sum(1, 2)

mock.expect.sum.called.withReturn(42)
mock.expect.sum.called.withReturn(match.gte(40))
mock.expect.sum.called.withReturn(match.number)

withReturn(undefined) matches calls that returned undefined:

typescript
mock.fire()
mock.expect.fire.called.withReturn(undefined)   // fire() returned nothing

calledOn(target) ​

Identity-check the this binding for at least one call:

typescript
const target = { tag: 'target' }
const fn = func<(x: number) => number>()
fn.call(target, 1)
fn.expect.called.calledOn(target)

threw(expected?) ​

Assert at least one call threw. The argument can be:

  • Omitted — any throw passes
  • A string — matches Error.message
  • An Error class — sugar for match.instanceOf(ErrorClass)
  • A matcher — e.g. match.objectContaining({ code: 1 }) for non-Error throws
typescript
mock.setup.fail.toThrow('bang')
try { mock.fail() } catch {}

mock.expect.fail.called.threw()                                    // any
mock.expect.fail.called.threw('bang')                              // message
mock.expect.fail.called.threw(Error)                               // class
mock.expect.fail.called.threw(match.instanceOf(Error))             // matcher
mock.expect.fail.called.threw(match.objectContaining({ code: 1 })) // structural

everyCall.* — all calls must match ​

Mirrors called.* but asserts every recorded call matches. Throws if the method was never called (no vacuous truths):

typescript
mock.greet('a')
mock.greet('b')
mock.greet('c')

mock.expect.greet.everyCall.withArg(match.string)
mock.expect.greet.everyCall.matchExactly(match.string)
mock.expect.greet.everyCall.withReturn(match.string)
mock.expect.greet.everyCall.threw(SomeError)

If the method was never called:

typescript
mock.expect.greet.everyCall.withArg('x')
// throws: Expected every call of greet but it was never called

Use .called.never() if you want the opposite assertion ("it must never have been called").

invocation(i) — target a specific call ​

Zero-indexed. Returns a smaller API with just withArg / withArgs:

typescript
mock.greet('first')
mock.greet('second', 'extra')

mock.expect.greet.invocation(0).withArg('first')
mock.expect.greet.invocation(1).withArg('second')
mock.expect.greet.invocation(1).withArgs('second', 'extra')

Out-of-range indexes throw immediately:

typescript
mock.greet('only')
mock.expect.greet.invocation(1).withArg('nope')
// throws: invocation out of range

Negation — .not.* ​

Every positive called.* assertion has a .not counterpart. It passes when the positive would have thrown:

typescript
mock.greet('alice')

mock.expect.greet.called.not.never()         // it WAS called
mock.expect.greet.called.not.twice()         // it was called once, not twice
mock.expect.greet.called.not.withArg('bob')  // 'bob' was never passed
mock.expect.greet.called.not.withReturn('goodbye')
mock.expect.greet.called.not.threw()

.not is available on called only (not on everyCall or invocation).

Resetting ​

Per-method ​

typescript
mock.expect.greet.called.reset()   // clear only greet's history

All methods at once ​

typescript
mock.called.reset()                // clear history on every method of this mock

Reset leaves behaviours (from setup) intact. To wipe both behaviours and history, use mock.restore(mock.snapshot()) at an earlier "clean" state, or see Lifecycle.

Failure messages ​

Every failure includes the recorded call history. Example:

AssertionError [ERR_ASSERTION]: Expected greet to be called with: 'carol'
  actual calls:
  #0 ('alice')
  #1 ('bob')
  #2 (42, { deep: true })

This is true for withArg, withArgs, withReturn, and the everyCall.* variants. See Diagnostics for more on improving failure output.

Released under the MIT License.