deps/undici/src/docs/docs/best-practices/writing-tests.md
Undici is tuned for a production use case and its default will keep a socket open for a few seconds after an HTTP request is completed to remove the overhead of opening up a new socket. These settings that makes Undici shine in production are not a good fit for using Undici in automated tests, as it will result in longer execution times.
The following are good defaults that will keep the socket open for only 10ms:
import { request, setGlobalDispatcher, Agent } from 'undici'
const agent = new Agent({
keepAliveTimeout: 10, // milliseconds
keepAliveMaxTimeout: 10 // milliseconds
})
setGlobalDispatcher(agent)
Undici's Client automatically reconnects after a socket error. This means
a test can silently disconnect, reconnect, and still pass. Unfortunately,
this could mask bugs like unexpected parser errors or protocol violations.
To catch these silent reconnections, add a disconnect guard after creating
a Client:
const { Client } = require('undici')
const { test, after } = require('node:test')
const { tspl } = require('@matteo.collina/tspl')
test('example with disconnect guard', async (t) => {
t = tspl(t, { plan: 1 })
const client = new Client('http://localhost:3000')
after(() => client.close())
client.on('disconnect', () => {
if (!client.closed && !client.destroyed) {
t.fail('unexpected disconnect')
}
})
// ... test logic ...
})
client.close() and client.destroy() both emit 'disconnect' events, but
those are expected. The guard only fails when a disconnect happens during the
active test (i.e., !client.closed && !client.destroyed is true).
Skip the guard for tests where a disconnect is expected behavior, such as:
signal.emit('abort'), ac.abort())res.destroy(), req.socket.destroy())data.body.destroy())HeadersTimeoutError, BodyTimeoutError)Client)HTTPParserError)