API tests with Supertest
🏠The everyday picture
A mystery shopper who walks in through the front door like any customer, places real orders, and reports exactly what happened at the counter.
📘What it really is
- Supertest sends real HTTP requests to your Express
appwithout starting a server on a port (that's whyappandserverwere separated in Module 42):
import request from "supertest";
import { app } from "../src/app.ts";
it("refuses to list enquiries when signed out", async () => {
const res = await request(app).get("/enquiries");
expect(res.status).toBe(401);
});
it("creates an enquiry with valid data", async () => {
const agent = request.agent(app); // keeps cookies, like a browser
await agent.post("/auth/sign-in").send({ email: "staff@test.in", password: "test-password-123" });
const res = await agent.post("/enquiries").send(validEnquiry);
expect(res.status).toBe(201);
expect(res.body.status).toBe("new");
});
it("returns field errors for invalid data", async () => {
const res = await signedInAgent.post("/enquiries").send({ name: "" });
expect(res.status).toBe(422);
expect(res.body.fields.email).toBeDefined();
});
-
API tests check what clients actually experience: status codes, response bodies, cookies, headers, and that error responses contain no stack traces.
-
Use a test configuration: a temporary data folder (or in-memory repository) and test users, so tests never touch real data.
🖱️Try it
Install supertest. Write API tests for: 401 when signed out; sign-in success and failure; create (201), invalid
create (422), get unknown (404); and one test that a 500 response contains no stack trace. Run them with the rest.
✅Quick check
Why can Supertest test the app without starting a server?
Show the answer
It uses the exported app directly, which is kept separate from the code that listens on a port.
What does request.agent(app) do that request(app) doesn't?
Show the answer
It keeps cookies between requests, like a browser, so you can sign in and then make signed-in requests.
