Configuring behaviour
Every mocked method has a .setup handle. Call a method on .setup to configure how the mock responds. Setup methods chain (return the setup object) so you can combine .when(...), .once()/.times(n), and .and.then fluently.
Return values
toReturn(value)
Return a fixed value on every invocation:
mock.setup.greet.toReturn('hello')
mock.greet('alice') // 'hello'
mock.greet('bob') // 'hello'toReturnSelf()
Return the wrapped mock itself — for fluent/chainable APIs like query builders:
const q = stub<{
where(v: unknown): unknown
orderBy(v: unknown): unknown
execute(): number[]
}>(['where', 'orderBy', 'execute'])
q.setup.where.toReturnSelf()
q.setup.orderBy.toReturnSelf()
q.setup.execute.toReturn([1, 2])
q.where('a').orderBy('b').where('c').execute() // [1, 2]toReturnInOrder(...values)
Return each value once, then hold the last value ("sticky-last"):
mock.setup.greet.toReturnInOrder('first', 'second', 'third')
mock.greet() // 'first'
mock.greet() // 'second'
mock.greet() // 'third'
mock.greet() // 'third' (sticky)Pass { then, cycle } as a trailing argument for alternative fallback strategies:
// Fallback to 'default' after exhausting the list
mock.setup.greet.toReturnInOrder('a', 'b', { then: 'default' })
// → 'a', 'b', 'default', 'default', …
// Cycle indefinitely
mock.setup.greet.toReturnInOrder('a', 'b', { cycle: true })
// → 'a', 'b', 'a', 'b', …Value vs options
Since the options detector checks for then or cycle keys, if you genuinely want to return an object that happens to have those keys, wrap your values in an array:
mock.setup.fn.toReturnInOrder([{ then: 'i-am-a-value' }])Custom implementations
toDoThis(fn)
Run arbitrary logic. Gets the call args, returns anything (including undefined):
mock.setup.greet.toDoThis((name: string) => `yo ${name}`)
mock.greet('alice') // 'yo alice'toThrow(message)
Throw a fresh Error with the given message:
mock.setup.parse.toThrow('malformed input')
mock.parse('x') // throws Error('malformed input')Promise behaviours
toResolveWith(value) / toResolve()
mock.setup.fetch.toResolveWith({ data: 42 })
await mock.fetch('/x') // { data: 42 }
mock.setup.save.toResolve()
await mock.save(data) // undefinedtoRejectWith(error)
mock.setup.fetch.toRejectWith(new Error('network'))
await mock.fetch('/x') // rejects with Error('network')toResolveInOrder / toRejectInOrder
Same sticky-last semantics as toReturnInOrder:
mock.setup.fetch.toResolveInOrder('a', 'b', 'c')
await mock.fetch() // 'a'
await mock.fetch() // 'b'
mock.setup.fetch.toRejectInOrder(err1, err2)toResolveAfter(ms, value) / toRejectAfter(ms, error)
Delay resolution. Pairs well with fake timers:
mock.setup.fetch.toResolveAfter(100, { data: 42 })
const p = mock.fetch('/x') // pending
// ... advance time by 100ms ...
await p // { data: 42 }toHang()
Return a never-settling promise — useful for testing timeout/cancellation paths:
mock.setup.fetch.toHang()
const p = mock.fetch('/x')
// p never resolves or rejectsIterators
toYield(...values)
Return a fresh sync iterator on every call:
mock.setup.stream.toYield(1, 2, 3)
for (const v of mock.stream()) console.log(v)
// 1
// 2
// 3toAsyncYield(...values)
Same for async iterators:
mock.setup.stream.toAsyncYield(1, 2, 3)
for await (const v of mock.stream()) console.log(v)toAsyncYieldThrow(error, ...valuesBefore)
Yield some values, then throw:
mock.setup.stream.toAsyncYieldThrow(new Error('drained'), 1, 2)
// yields 1, 2, then rejects with Error('drained')Callbacks and events
toCallbackWith(...args)
Finds the last function argument and invokes it with the provided args — handy for Node-style callback APIs:
mock.setup.load.toCallbackWith(null, 'data')
mock.load('file.txt', (err, data) => {
// err === null, data === 'data'
})toEmit(eventName, ...params)
Emit an event on the wrapped object when the method is called:
mock.setup.greet.toEmit('greeted', 'payload')
mock.on('greeted', (data) => console.log(data)) // 'payload'
mock.greet('alice')toIntercept(fn)
Call your interceptor and then the original method (preserving the return value):
const log: unknown[][] = []
wrapped.setup.greet.toIntercept((...args) => log.push(args))
wrapped.greet('alice')
// log === [['alice']]
// wrapped.greet still returned the original resulttoTimeWarp(ms)
Accelerate a callback — schedules the found callback with the given delay instead of the original:
mock.setup.load.toTimeWarp(0)
mock.load(10_000, (result) => { /* called immediately, not after 10s */ })Gating behaviour by arguments
.when(value | matcher | predicate)
Chain before any behaviour to gate it on the call's arguments:
// Value match (deep equal)
mock.setup.greet.when('alice').toReturn('hi alice')
mock.setup.greet.when('bob').toReturn('hi bob')
// Matcher
mock.setup.greet.when(match.string).toReturn('hi string')
// Multiple positional args / matchers
mock.setup.log.when('info', match.string).toReturn(true)
// Predicate function (full access to args)
mock.setup.greet.when((args) => args[0].startsWith('Dr')).toReturn('hi doctor')See Matchers for the full match.* catalogue.
Limiting invocations
.once() / .twice() / .times(n)
Chain to limit how many times a behaviour applies:
mock.setup.greet.once().toReturn('first')
mock.setup.greet.toReturn('default')
mock.greet() // 'first'
mock.greet() // 'default'The limit counts each matching invocation, so a time-limited behaviour combined with when only consumes when its predicate matches:
mock.setup.greet.when('admin').twice().toReturn('hi admin')
mock.setup.greet.toReturn('default')
mock.greet('alice') // 'default' (when predicate didn't match, quota unchanged)
mock.greet('admin') // 'hi admin' (1/2)
mock.greet('admin') // 'hi admin' (2/2)
mock.greet('admin') // 'default' (quota exhausted)Chaining sequential behaviours
.and.then is an alias for the setup object — use it for readability when building a sequence:
mock.setup.greet
.toReturn('alice')
.twice()
.and.then
.toReturn('sally')
mock.greet() // 'alice'
mock.greet() // 'alice'
mock.greet() // 'sally'Can be combined with .when:
mock.setup.greet
.when('simon')
.toReturn('special')
.twice()
.and.then
.toReturn('default')
mock.greet('simon') // 'special'
mock.greet('simon') // 'special'
mock.greet('simon') // 'default'Clearing behaviour
.fallback()
Clear all configured behaviours on this method; future calls fall back to the original (or undefined for pure stubs):
wrapped.setup.greet.toReturn('mocked')
wrapped.greet('x') // 'mocked'
wrapped.setup.greet.fallback()
wrapped.greet('x') // original return valueThe dispatch rule (refresher)
Time-limited behaviours first, in registration order (FIFO). When none match, the last registered unlimited behaviour wins. See the Philosophy for why and how to work with it.