Fire Real HTTP Requests Without Binding a Port
HTTP Testing with Supertest
Supertest wraps app.callback() so you can make real HTTP assertions against your Koa routes without occupying a port or managing server lifecycle.
What you'll learn
- Pass app.callback() to supertest to create a test agent
- Assert on status codes, headers, and JSON response bodies
- Test authenticated routes by setting request headers
Supertest is the standard library for integration-testing Node HTTP servers. It
accepts any function with the signature (req, res) => void — exactly what
app.callback() returns — so it works with Koa without any adapter.
Installation
npm i -D supertest Basic Request and Assertion
// app.js
import Koa from "koa";
import Router from "@koa/router";
const app = new Koa();
const router = new Router();
router.get("/ping", (ctx) => {
ctx.body = { ok: true };
});
app.use(router.routes());
export default app; // app.test.js
import request from "supertest";
import app from "./app.js";
describe("GET /ping", () => {
it("returns 200 with ok:true", async () => {
const res = await request(app.callback()).get("/ping");
expect(res.status).toBe(200);
expect(res.body).toEqual({ ok: true });
});
}); app.callback() hands Supertest the underlying Node http.IncomingMessage
handler. No app.listen() call is needed, so no port is bound and no teardown
is required.
Asserting Headers and Errors
it("returns 404 for unknown routes", async () => {
const res = await request(app.callback()).get("/unknown");
expect(res.status).toBe(404);
});
it("echoes the Accept-Language header", async () => {
const res = await request(app.callback())
.get("/ping")
.set("Accept-Language", "fr");
expect(res.headers["content-type"]).toMatch(/json/);
}); Testing Authenticated Routes
Pass your token or cookie in .set() or .auth():
it("rejects requests without a token", async () => {
const res = await request(app.callback()).get("/api/me");
expect(res.status).toBe(401);
});
it("accepts a valid Bearer token", async () => {
const res = await request(app.callback())
.get("/api/me")
.set("Authorization", "Bearer test-token-abc");
expect(res.status).toBe(200);
}); Reusing an Agent
If your routes set cookies, use request.agent() to persist them across calls:
const agent = request.agent(app.callback());
await agent.post("/login").send({ user: "ada", pass: "secret" });
const res = await agent.get("/dashboard"); // cookie is sent automatically
expect(res.status).toBe(200); Up Next
Capture structured logs from every request with Pino and koa-pino-logger.
Structured Logging →