When You Need Real Sockets
supertest With Fastify
For integration tests that exercise the real network stack, supertest pairs with app.server after ready().
What you'll learn
- Boot the app with await app.ready()
- Pass app.server to supertest
- Assert on real HTTP responses
app.inject() is great for unit and most integration tests. But some plugins, middleware, or
proxies behave differently with a real socket — for those, use supertest against
app.server after the app is ready().
Setup
npm i -D supertest @types/supertest A supertest Test
import { test } from 'node:test';
import request from 'supertest';
import { buildApp } from '../src/app.js';
test('GET / over a real socket', async () => {
const app = buildApp();
await app.ready();
await request(app.server)
.get('/')
.expect(200)
.expect('content-type', /json/);
await app.close();
}); await app.ready() runs all plugin registrations without binding a port. supertest then spins
up an ephemeral listener around app.server for the duration of the request.
When to Reach For This
- Testing TLS, HTTP/2, or socket-level timeouts
- Verifying proxy headers (
X-Forwarded-For) flow correctly - Reproducing a bug that only shows up over the wire
For most route logic, prefer app.inject() — supertest is slower and the assertions are
chainable but less flexible than asserting on a returned response object.