SnapshotAgent records real HTTP responses and replays them on later requests,
allowing tests to run against deterministic, captured data instead of a live
network. It extends MockAgent, so it can be installed with
setGlobalDispatcher() or passed explicitly through the dispatcher
option, and it works with every undici API (fetch, request, stream,
pipeline, and so on).
A SnapshotAgent operates in one of three modes. In 'record' mode it performs
real requests and writes the responses to a snapshot file. In 'playback' mode
it serves responses from the snapshot file without touching the network. In
'update' mode it replays existing snapshots and records any request that has no
matching snapshot.
import { SnapshotAgent, setGlobalDispatcher, fetch } from 'undici'
const agent = new SnapshotAgent({
mode: 'record',
snapshotPath: './snapshots/api.json'
})
setGlobalDispatcher(agent)
const response = await fetch('https://api.example.com/users')
const users = await response.json()
await agent.close()Constructing a SnapshotAgent emits an ExperimentalWarning once per process.
class SnapshotAgent extends MockAgentThe captured interactions are managed by an internal SnapshotRecorder, which
is accessible through agent.getRecorder(). Because SnapshotAgent extends
MockAgent, all MockAgent and Dispatcher methods and events are also
available.
new SnapshotAgent(options?): void<Object>MockAgent
options plus the
following snapshot-specific options.<string>'record'
,
'playback'
, or
'update'
.
Default:
'record'
.<string>mode
is
'playback'
or
'update'
.
Default:
null
.<number>Infinity
.<boolean>true
, snapshots are written to
snapshotPath
automatically as they are recorded.
Default:
false
.<number>autoFlush
is enabled.
Default:
30000
.<string><string><string><boolean>true
, the request body is included in request
matching.
Default:
true
.<Function>matchBody
is
true
.<string>
|
<Buffer>
|
<null>
|
<undefined><string><boolean>true
, query parameters are included in request
matching.
Default:
true
.<Function>matchQuery
is
true
.<URLSearchParams><string><boolean>true
, header matching is case-sensitive.
Default:
false
.<Function>'record'
and
'update'
modes.<Function>'playback'
and
'update'
modes.Creates a new SnapshotAgent. Throws an InvalidArgumentError when mode
is 'playback' or 'update' and snapshotPath is not provided, and throws an
InvalidArgumentError when mode is not one of the supported values.
When mode is 'playback' or 'update' and snapshotPath is set, snapshots
are loaded from the file automatically; a missing file is ignored until the first
request requires it.
import { SnapshotAgent, setGlobalDispatcher } from 'undici'
const agent = new SnapshotAgent({
mode: 'playback',
snapshotPath: './snapshots/api.json'
})
setGlobalDispatcher(agent)The mode controls how requests are handled:
-
'record'performs the real request and stores the response.import { SnapshotAgent, setGlobalDispatcher, fetch } from 'undici' const agent = new SnapshotAgent({ mode: 'record', snapshotPath: './snapshots/api.json' }) setGlobalDispatcher(agent) await fetch('https://api.example.com/users') await agent.saveSnapshots() -
'playback'serves the recorded response without any network access. A request with no matching snapshot rejects with anUndiciErrorwhose message begins withNo snapshot found.import { SnapshotAgent, setGlobalDispatcher, fetch } from 'undici' const agent = new SnapshotAgent({ mode: 'playback', snapshotPath: './snapshots/api.json' }) setGlobalDispatcher(agent) const response = await fetch('https://api.example.com/users') -
'update'replays an existing snapshot when one is found and otherwise records the request like'record'mode.import { SnapshotAgent, setGlobalDispatcher, fetch } from 'undici' const agent = new SnapshotAgent({ mode: 'update', snapshotPath: './snapshots/api.json' }) setGlobalDispatcher(agent) await fetch('https://api.example.com/new-endpoint')
agent.loadSnapshots(filePath?): Promise<void><string>snapshotPath
given to the constructor.Loads snapshots from disk into memory. In 'playback' mode this also installs
the corresponding MockAgent interceptors so that subsequent requests are
matched against the loaded snapshots.
await agent.loadSnapshots('./snapshots/api.json')agent.saveSnapshots(filePath?): Promise<void><string>snapshotPath
given to the constructor.Writes all recorded snapshots to disk.
await agent.saveSnapshots('./snapshots/api.json')agent.getRecorder(): SnapshotRecorder<SnapshotRecorder>Returns the underlying SnapshotRecorder. The recorder exposes lower-level
operations over the captured data, including record(), findSnapshot(),
getSnapshots(), size(), and clear(). The recorder is an internal type
returned for inspection; it is not exported from the package root.
const recorder = agent.getRecorder()
console.log(`Recorded ${recorder.size()} interactions`)agent.getMode(): string<string>'record'
,
'playback'
, or
'update'
.Returns the mode the agent was constructed with.
agent.clearSnapshots(): undefined<undefined>Removes all snapshots from memory.
agent.clearSnapshots()agent.resetCallCounts(): undefined<undefined>Resets the call count of every snapshot to zero. Useful between tests when the
same agent is reused, since matching a snapshot in 'playback' mode increments
its call count.
agent.resetCallCounts()agent.deleteSnapshot(requestOpts): boolean<Object><boolean>true
if a matching snapshot was deleted,
false
if none
was found.Deletes a single snapshot that matches the given request options.
const deleted = agent.deleteSnapshot({
method: 'GET',
origin: 'https://api.example.com',
path: '/users'
})agent.getSnapshotInfo(requestOpts): Object | null<Object>null
when
none is found.Returns metadata about a snapshot without returning the response payload.
const info = agent.getSnapshotInfo({
method: 'GET',
origin: 'https://api.example.com',
path: '/users'
})agent.replaceSnapshots(snapshotData): undefined<undefined>Replaces all in-memory snapshots with the provided data.
const recorder = agent.getRecorder()
const snapshots = recorder.getSnapshots()
agent.replaceSnapshots(snapshots.map((snapshot, index) => ({
hash: `snapshot-${index}`,
snapshot
})))agent.close(): Promise<void>Closes the agent and releases its resources. In 'record' and 'update' modes
the recorder is flushed to disk first. In 'playback' mode nothing is written,
because matching a snapshot mutates its call count and saving would needlessly
rewrite the snapshot file. The real Agent, when one was created, and the
underlying MockAgent are closed as well.
await agent.close()