Skip to main content

Documentation Index

Fetch the complete documentation index at: https://mintlify.com/nodejs/undici/llms.txt

Use this file to discover all available pages before exploring further.

MockAgent is the primary entry point for HTTP mocking in undici. It extends Dispatcher and wraps a real Agent, intercepting requests before they hit the network so your tests never depend on external services. When set as the global dispatcher, every request() or fetch() call in your process routes through the mock layer automatically.

Constructor

Instantiating MockAgent
import { MockAgent } from 'undici'

const mockAgent = new MockAgent()
options
object
Options passed to the underlying Agent. All AgentOptions are supported in addition to the following.

Activation

Setting as the global dispatcher

The most common pattern is to install MockAgent as the global dispatcher so all requests in the process go through it:
Global MockAgent setup
import { MockAgent, setGlobalDispatcher } from 'undici'

const mockAgent = new MockAgent()
setGlobalDispatcher(mockAgent)

mockAgent.get(origin)

Returns the MockPool (or MockClient when connections === 1) for the given origin, creating it if it does not already exist. This is the object on which you register interceptors.
origin
string | RegExp | (value: string) => boolean
required
The origin to retrieve or create a mock dispatcher for.
Matcher typeCondition to pass
stringExact match
RegExpRegex must pass
FunctionMust return true
returns
MockClient | MockPool
Returns MockClient when connections === 1, otherwise MockPool.
Getting a MockPool for an origin
const mockPool = mockAgent.get('http://localhost:3000')
Origin matching with RegExp
const mockPool = mockAgent.get(new RegExp('http://localhost:3000'))
Origin matching with a function
const mockPool = mockAgent.get((origin) => origin === 'http://localhost:3000')

mockAgent.activate() / mockAgent.deactivate()

MockAgent is active by default. Call deactivate() to disable mocking temporarily and activate() to re-enable it.
Temporarily disabling mocks
mockAgent.deactivate()
// Requests pass through to the real network (if net connect is enabled)

mockAgent.activate()
// Mocking is restored

Interceptors

Interceptors define the rules for matching requests and the responses to return. You register them on a MockPool or MockClient obtained from mockAgent.get().

mockPool.intercept(options)

Returns a MockInterceptor that you chain to define the response.
options
object
required
returns
MockInterceptor
A MockInterceptor instance. Chain the methods below to define the response.

mockInterceptor.reply(statusCode, body, options?)

Defines the response returned when the interceptor matches.
statusCode
number
required
HTTP status code for the mocked response.
body
string | Buffer | object | (opts: RequestInfo) => body
Response body. Objects are serialized as JSON. Strings and buffers are sent as-is. Pass a callback to compute the body dynamically from the incoming request — the callback receives { method, url, body, headers, origin }.
options
object

mockInterceptor.reply(callback)

Alternative signature — pass a single callback that returns the full reply options:
Reply with a full options callback
mockPool.intercept({ path: '/echo', method: 'GET' }).reply(({ headers }) => ({
  statusCode: 200,
  data: { message: headers.get('message') }
}))

mockInterceptor.replyWithError(error)

Causes the matched request to throw the given error.
Simulating a network error
mockPool.intercept({ path: '/failing' }).replyWithError(new Error('kaboom'))

mockInterceptor.defaultReplyHeaders(headers)

Sets headers that are merged into every subsequent reply() on this interceptor.
headers
Record<string, string>
required
Default response headers.

mockInterceptor.defaultReplyTrailers(trailers)

Sets trailers that are merged into every subsequent reply() on this interceptor.
trailers
Record<string, string>
required
Default response trailers.

mockInterceptor.replyContentLength()

Automatically calculates and includes a content-length header in the response.

MockScope — controlling repeat behaviour

reply(), replyWithError(), and the other reply methods return a MockScope. Use the following methods to control how many times the interceptor fires:
persist()
MockScope
The interceptor matches indefinitely. Without this, each interceptor matches once by default.
times(n)
MockScope
The interceptor matches exactly n times. Overridden by persist().
delay(ms)
MockScope
Delays the reply by ms milliseconds.

Matcher types

All path, method, body, and individual header values accept three matcher types:
TypeBehaviour
stringExact equality check
RegExpregex.test(value) must be true
FunctionFunction must return true
Mixed matchers on an interceptor
mockPool.intercept({
  path: '/foo',
  method: /^GET$/,
  body: (value) => value === 'form=data',
  headers: {
    'User-Agent': 'undici',
    Host: /^example\.com$/
  }
}).reply(200, 'matched')

Network control

mockAgent.disableNetConnect()

Causes any request that does not match a registered interceptor to throw a MockNotMatchedError. Use this in tests to ensure every network call is explicitly mocked.
Disabling real network access
mockAgent.disableNetConnect()

mockAgent.enableNetConnect(host?)

Allows unmatched requests to reach the real network, optionally restricted to specific hosts. Calling this without arguments allows all real requests.
host
string | RegExp | (value: string) => boolean
When provided, only requests matching this host are allowed through. When omitted, all requests are permitted. Call multiple times to allow multiple hosts.
Allowing specific hosts to bypass mocking
mockAgent.enableNetConnect('example-1.com')
mockAgent.enableNetConnect('example-2.com:8080')

Assertions

mockAgent.assertNoPendingInterceptors(options?)

Throws an UndiciError if any registered interceptors have not been fully consumed. An interceptor is pending when it was registered but:
  • has not been invoked at all (single-use default), or
  • is persistent (.persist()) but was never called, or
  • has fewer invocations remaining than times(n) requires.
Use this at the end of a test to ensure all expected requests were made.
Asserting all interceptors were consumed
agent.assertNoPendingInterceptors()
// Throws with a table listing pending interceptors if any remain

mockAgent.pendingInterceptors()

Returns an array of all pending interceptors without throwing. Each entry is a MockDispatch augmented with an origin field.

Call history

Call history must be enabled either at construction time via enableCallHistory: true or by calling mockAgent.enableCallHistory() afterwards.

Enabling history

Enabling call history at construction
const mockAgent = new MockAgent({ enableCallHistory: true })
Enabling call history after construction
const mockAgent = new MockAgent()
mockAgent.enableCallHistory()

mockAgent.getCallHistory()

Returns the MockCallHistory instance, or undefined if history was never enabled.

MockCallHistory methods

calls()
MockCallHistoryLog[]
Returns all recorded call logs as an array.
firstCall()
MockCallHistoryLog | undefined
Returns the first recorded call.
lastCall()
MockCallHistoryLog | undefined
Returns the most recent recorded call.
nthCall(n)
MockCallHistoryLog | undefined
Returns the nth call (1-indexed). Throws if n is not a positive integer.
filterCalls(criteria, options?)
MockCallHistoryLog[]
Filters recorded calls. criteria can be a function, RegExp, or an object with MockCallHistoryLog property keys. The optional options.operator is 'OR' (default) or 'AND'.
filterCallsByPath(value)
MockCallHistoryLog[]
Filters by path. Accepts a string or RegExp.
filterCallsByMethod(value)
MockCallHistoryLog[]
Filters by HTTP method. Accepts a string or RegExp.
filterCallsByOrigin(value)
MockCallHistoryLog[]
Filters by origin. Accepts a string or RegExp.
filterCallsByFullUrl(value)
MockCallHistoryLog[]
Filters by the full URL including query string and hash. Accepts a string or RegExp.
clear()
void
Clears all recorded logs. Called automatically by mockAgent.close().

MockCallHistoryLog properties

Each log entry exposes the following properties:
method
string
HTTP method of the request.
origin
string
Protocol and host, e.g. 'http://example.com'.
path
string
Path portion of the URL, always starting with /.
fullUrl
string
Complete URL including protocol, host, path, query, and hash.
searchParams
Record<string, string>
Parsed query parameters as a plain object.
headers
object | undefined
Request headers.
body
any
Request body.
protocol
string
Protocol string, e.g. 'http:'.
host
string
Host and port, e.g. 'example.com:3000'.
port
string
Port number as a string, or an empty string if absent.
hash
string
URL hash including #, or an empty string if absent.

Clearing call history

Clearing all recorded calls
mockAgent.clearCallHistory()
// equivalent to:
mockAgent.getCallHistory()?.clear()

Errors

MockNotMatchedError

Thrown when disableNetConnect() is active and a request does not match any registered interceptor.
Importing MockNotMatchedError
import { mockErrors } from 'undici'

const { MockNotMatchedError } = mockErrors
// error.code === 'UND_MOCK_ERR_MOCK_NOT_MATCHED'

Cleanup

Call mockAgent.close() after tests complete. This clears call history and closes all underlying mock pools and clients.
Closing MockAgent after tests
await mockAgent.close()

Complete example

The following example uses node:test to verify a JSON API endpoint end-to-end with no real network traffic:
Full test with MockAgent and node:test
import { test } from 'node:test'
import assert from 'node:assert/strict'
import { MockAgent, setGlobalDispatcher, getGlobalDispatcher, request } from 'undici'

test('GET /users returns a list of users', async (t) => {
  const original = getGlobalDispatcher()
  const mockAgent = new MockAgent()
  mockAgent.disableNetConnect()
  setGlobalDispatcher(mockAgent)

  t.after(async () => {
    await mockAgent.close()
    setGlobalDispatcher(original)
  })

  const mockPool = mockAgent.get('https://api.example.com')

  mockPool
    .intercept({ path: '/users', method: 'GET' })
    .reply(200, [{ id: 1, name: 'Alice' }], {
      headers: { 'content-type': 'application/json' }
    })

  const { statusCode, body } = await request('https://api.example.com/users')

  assert.equal(statusCode, 200)
  const data = await body.json()
  assert.equal(data[0].name, 'Alice')

  // Verify every registered interceptor was consumed
  mockAgent.assertNoPendingInterceptors()
})
Restore the original global dispatcher in a cleanup hook (t.after or afterEach) so mocks do not leak between tests.

Build docs developers (and LLMs) love