Integration tests verify that multiple units work together — database queries, service calls, and HTTP handlers. Today you will test an Express API with Supertest and a real SQLite database.
npm install --save-dev supertest
# Use jest with --testEnvironment=node for API tests// app.js — Express app (exported without .listen)
import express from 'express';
const app = express();
app.use(express.json());
app.get('/users/:id', async (req, res) => {
const user = await db.findUser(req.params.id);
if (!user) return res.status(404).json({ error: 'Not found' });
res.json(user);
});
app.post('/users', async (req, res) => {
const { name, email } = req.body;
if (!name || !email) return res.status(400).json({ error: 'Missing fields' });
const user = await db.createUser({ name, email });
res.status(201).json(user);
});
export default app;// app.test.js
import request from 'supertest';
import app from './app';
import { db } from './db';
beforeAll(() => db.migrate()); // run migrations
afterEach(() => db.truncate()); // clean slate
afterAll(() => db.close());
describe('GET /users/:id', () => {
test('returns 404 for unknown user', async () => {
const res = await request(app).get('/users/999');
expect(res.status).toBe(404);
expect(res.body.error).toBe('Not found');
});
test('returns user by ID', async () => {
const user = await db.createUser({ name: 'Bo', email: '[email protected]' });
const res = await request(app).get(\`/users/\${user.id}\`);
expect(res.status).toBe(200);
expect(res.body.name).toBe('Bo');
});
});
describe('POST /users', () => {
test('creates a user', async () => {
const res = await request(app)
.post('/users')
.send({ name: 'Alice', email: '[email protected]' });
expect(res.status).toBe(201);
expect(res.body).toHaveProperty('id');
});
test('rejects missing fields', async () => {
const res = await request(app).post('/users').send({ name: 'No email' });
expect(res.status).toBe(400);
});
});