After this lesson, you will be able to: Test multiple components together, including real database interactions and API routes, without falling into the over-mocking trap.
Unit tests prove each piece works alone. Integration tests prove the pieces work together, which is where a huge share of real bugs hide: the API route that calls the database, the serializer that mangles a date, the transaction that does not roll back. This lesson covers testing across a boundary with a real (test) database.
Unit tests mock the database, so they never catch a wrong SQL query, a missing migration, a serialization bug, or a broken transaction. Integration tests run the real code path against a real (but disposable) dependency. The cost is speed: they run in seconds, not milliseconds, so you write fewer of them and target the boundaries that matter most: API route to database, service to external dependency, queue producer to consumer.
Use a dedicated test database (a Docker Postgres or a fast local SQLite). Reset state between tests. Never touch production.
// orders.integration.test.tsimport { beforeEach, afterAll, describe, it, expect } from 'vitest';import { db } from './db';import { createOrder, listOrdersForUser } from './orders';beforeEach(async () => {// Clean slate so tests don't depend on each other.await db.query('TRUNCATE orders RESTART IDENTITY CASCADE');});afterAll(async () => { await db.end(); });describe('orders (integration)', () => {it('persists and reads back an order', async () => {await createOrder({ userId: 'u1', total: 42.0 });const list = await listOrdersForUser('u1');expect(list).toHaveLength(1);expect(list[0].total).toBe(42.0);});it('isolates orders by user', async () => {await createOrder({ userId: 'u1', total: 10 });await createOrder({ userId: 'u2', total: 20 });expect(await listOrdersForUser('u1')).toHaveLength(1);});});
Drive the HTTP layer directly with a client like Supertest (Node) or httpx/TestClient (Python). This tests routing, validation, handler, and DB together.
// api.integration.test.tsimport request from 'supertest';import { app } from './app';it('rejects an invalid payload with 400', async () => {const res = await request(app).post('/api/orders').send({ total: -5 });expect(res.status).toBe(400);});it('creates an order and returns 201', async () => {const res = await request(app).post('/api/orders').send({ userId: 'u1', total: 42 });expect(res.status).toBe(201);expect(res.body).toMatchObject({ userId: 'u1', total: 42 });});
Pick the best answer.
Sign in and purchase access to unlock this lesson.