Skip to content

Decision tree

Which API to reach for, by task. Tables, not prose. If your question isn't answered below, it's either not a common task or you need the full Guide.

"I want to mock X" → which factory?

What you haveFactoryExample
A TypeScript interface or type, no instancestub<T>(['method1', 'method2'])stub<Database>(['query'])
An existing object instancestub(obj)stub(new Logger())
A class (want prototype methods auto-discovered)stub(MyClass)stub(Greeter)
A class (want static methods instead)stub(MyClass, undefined, { debug:{prefix:'deride',suffix:'stub'}, static:true })stub(Greeter, undefined, {…, static:true})
Code that does new MyClass(...) somewhere and you want to interceptstub.class(MyClass)stub.class(Database)
An existing object where real methods should run by defaultwrap(obj)wrap(realLogger)
An existing standalone functionwrap(fn) or func(fn) (identical)wrap(handler)
A brand-new standalone function from scratchfunc<F>()func<(x: number) => number>()

Rules of thumb:

  • stub replaces, wrap preserves real behaviour until overridden.
  • stub.class is only for new-call interception. If you control the call site, inject a stub(...) instance instead.
  • Use func() when the dependency is itself a function (callback, handler, fetcher).

"I want the method to return X" → which setup?

BehaviourSetup
Return a fixed value.toReturn(value)
Return the mock itself (fluent APIs).toReturnSelf()
Run custom logic with access to args.toDoThis((a, b) => …)
Throw an Error(msg).toThrow(message)
Return a resolved Promise with a value.toResolveWith(value)
Return a resolved Promise with no value.toResolve()
Return a rejected Promise.toRejectWith(error)
Resolve after N ms (use with fake timers).toResolveAfter(ms, value)
Reject after N ms.toRejectAfter(ms, error)
Return a Promise that never settles.toHang()
Return each value in a sequence (sticky-last).toReturnInOrder(a, b, c)
Same for promises.toResolveInOrder(…) / .toRejectInOrder(…)
Return a fresh sync iterator.toYield(1, 2, 3)
Return a fresh async iterator.toAsyncYield(1, 2, 3)
Async iterator that throws partway.toAsyncYieldThrow(err, v1, v2)
Invoke the last callback argument.toCallbackWith(err, data)
Emit an event on the wrapped object.toEmit(eventName, …args)
Run a side-effect then run the real method.toIntercept((…args) => { … })
Accelerate a callback-based timeout.toTimeWarp(ms)
Clear all configured behaviours.fallback()

"I want to gate a behaviour" → which modifier?

ConditionModifier (chain BEFORE the behaviour)
Only when first arg equals X.when(X)
Only when first arg passes matcher.when(match.string)
Only when multiple positional args match.when(match.string, match.number)
Only when custom predicate on args returns true.when((args) => …)
Only the next call.once()
Only the next 2 calls.twice()
Only the next N calls.times(n)

Chain multiple behaviours in sequence:

typescript
mock.setup.greet
  .when('alice').toReturn('hi alice')
  .once()
  .and.then
  .toReturn('default')

"I want to match an argument" → which matcher?

Import from deride: import { match } from 'deride'

AssertionMatcher
Any value, including nullishmatch.any
Not undefined (null OK)match.defined
Exactly null or undefinedmatch.nullish
typeof string / number / boolean / bigint / symbol / functionmatch.string / match.number / etc.
Is an arraymatch.array
Is a plain object (non-null, non-array)match.object
instanceof some classmatch.instanceOf(Ctor)
Object containing these keys with these values (matcher-aware)match.objectContaining({ id: match.number })
Array containing these items (any order)match.arrayContaining([1, 2])
Strict deep equal, no extra keysmatch.exact(value)
Number / bigint > < >= <=match.gt(n) / match.gte(n) / match.lt(n) / match.lte(n)
Number / bigint in range, inclusivematch.between(low, high)
String matches regexmatch.regex(/pattern/)
String starts/ends/includesmatch.startsWith(s) / match.endsWith(s) / match.includes(s)
NOT matching mmatch.not(m)
ALL of (m1, m2, …)match.allOf(m1, m2)
AT LEAST ONE of matcher listmatch.oneOf(m1, m2)
Equal to OR matches any of these valuesmatch.anyOf(v1, v2, …)
Custom predicate, namedmatch.where(v => …, 'description')

Matchers compose and nest — use them inside objectContaining, arrayContaining, exact, or anywhere a value goes.

"I want to assert how it was called" → which expect?

QuestionAssertion
Was it called exactly N times?.called.times(n) / .once() / .twice() / .never()
Call count < / <= / > / >= N.called.lt(n) / .lte(n) / .gt(n) / .gte(n)
Any call had this arg (partial deep match).called.withArg(arg)
Any call had these args (all present).called.withArgs(a, b)
Any call's args matched a regex (deep).called.withMatch(/regex/)
Any call had EXACTLY these args (strict deep equal).called.matchExactly(a, b)
Any call returned this value (or matcher).called.withReturn(value)
Any call was made on this this.called.calledOn(target)
Any call threw (optional: Error message / class / matcher).called.threw(expected?)
The i-th call included this arg.invocation(i).withArg(arg)
EVERY call matched (each of the above).everyCall.withArg(…) etc.
Negate any of the above.called.not.withArg(...)

Decision rule: if you want a test to fail when the assertion fails, use expect.called.*. If you want a boolean / data / to branch, use spy.*.

"I want to read call history" → spy surface

NeedReach for
"Was it called with X?" as a booleanmock.spy.method.calledWith(x)
Total call countmock.spy.method.callCount
All recorded callsmock.spy.method.calls
First / last recorded callmock.spy.method.firstCall / lastCall
A captured return valuemock.spy.method.lastCall?.returned
A captured this bindingmock.spy.method.lastCall?.thisArg
A captured sync throwmock.spy.method.lastCall?.threw
Pretty-print the full call log for debuggingmock.spy.method.printHistory()
Stable snapshot-friendly dumpmock.spy.method.serialize()

For async return values, lastCall.returned is the Promise. await it to get the settled value.

"I need to assert cross-mock ordering" → inOrder

NeedReach for
Assert spies fired in this order (first call of each)inOrder(a.spy.x, b.spy.y, c.spy.z)
Order a specific invocation of a spyinOrder(inOrder.at(a.spy.x, 0), b.spy.y)
Strict: no extra calls on listed spies betweeninOrder.strict(a.spy.x, b.spy.y)

"I need test lifecycle helpers" → sandbox / snapshot

NeedReach for
Reset call history on every mock between testssandbox().reset() in afterEach
Full wipe (history + behaviours)sandbox().restore() in afterAll
Save/restore a single mock's state mid-testmock.snapshot() / mock.restore(snap)
Reset ONE mock's historymock.called.reset()

"I need fake timers" → deride/clock

typescript
import { useFakeTimers, isFakeTimersActive, restoreActiveClock } from 'deride/clock'
NeedReach for
Install fake Date / setTimeout / setInterval / queueMicrotaskconst clock = useFakeTimers()
Advance time by N ms and fire due timersclock.tick(ms)
Drain all pending timersclock.runAll() (throws if setInterval would loop)
Drain microtasksclock.flushMicrotasks()
Read captured callback errorsclock.errors
Restore native globalsclock.restore()
Safety net in afterEachif (isFakeTimersActive()) restoreActiveClock()

"I need framework matchers" → deride/vitest or deride/jest

typescript
import 'deride/vitest'   // or 'deride/jest' — side-effect import, registers matchers
MatcherSemantics
expect(mock.spy.x).toHaveBeenCalled()>= 1 call
.toHaveBeenCalledTimes(n)exactly N
.toHaveBeenCalledOnce()exactly 1
.toHaveBeenCalledWith(...args)any call's args match
.toHaveBeenLastCalledWith(...args)last call's args match
.toHaveBeenNthCalledWith(n, ...args)n is 1-indexed

Works on a MethodSpy (mock.spy.greet) or a MockedFunction proxy directly.

Released under the MIT License.