| name | SuperTest API Testing |
| description | Test Node.js HTTP APIs in-process with SuperTest — request(app) without binding a port, chained .expect assertions, auth headers, JSON body validation, and Jest integration with proper async/await patterns. |
| version | 1.0.0 |
| author | thetestingacademy |
| license | MIT |
| tags | ["supertest","api-testing","express","nodejs","jest","integration-testing","http","rest","assertions"] |
| testingTypes | ["api","integration"] |
| frameworks | ["jest","supertest","express"] |
| languages | ["typescript","javascript"] |
| domains | ["api"] |
| agents | ["claude-code","cursor","github-copilot","windsurf","codex","aider","continue","cline","zed","bolt","gemini-cli","amp"] |
SuperTest API Testing
This skill makes an AI agent write integration tests for Express/Koa/Fastify-compatible Node HTTP apps using SuperTest: pass the app object directly to request() so no port is bound, chain .expect() for status/header checks, and assert response bodies with Jest matchers. Trigger it when a Node project exposes an Express app, when the user asks to test REST endpoints without spinning up a server, or when supertest is already in devDependencies.
Core Principles
- Test the app object, not a running server.
request(app) binds to an ephemeral port per request and tears it down — no app.listen(), no port conflicts, no orphaned servers in CI.
- Export
app separately from the listener. The single biggest enabler: app.ts exports the Express app, server.ts calls listen(). Tests import app.ts only.
- Always
await (or return) the request chain. A SuperTest call is a thenable; forgetting await means the test passes before the request even fires.
.expect(status) for transport, Jest matchers for payload. Status codes and content-type belong in the chain; body shape belongs in expect(res.body).toMatchObject(...) where failure diffs are readable.
- Real database or none — never half-mocked. Either run integration tests against a disposable database (Testcontainers, SQLite in-memory) or mock the data layer entirely. Mocking two of five queries gives you tests that lie.
- Each test owns its data. Create the records a test needs inside the test (or a
beforeEach), and make cleanup idempotent. Order-dependent suites rot within a sprint.
Setup
npm install --save-dev supertest @types/supertest jest ts-jest @types/jest
The app/server split that makes everything testable:
import express from 'express';
import { usersRouter } from './routes/users';
(): express. {
app = ();
app.(express.());
app.(, usersRouter);
app.(, res.({ : }));
app;
}