- Test pyramid cho Node.js API: unit tests (service/utils logic) → integration tests (controller + real DB) → e2e tests (full HTTP).
- Controller testing với supertest:
const res = await request(app).post('/users').send({ email: 'test@test.com' }); expect(res.status).toBe(201)— không cần start server, supertest inject request trực tiếp. - Service testing: mock DB layer với
jest.mock('../db')→(db.findUser as jest.Mock).mockResolvedValue({ id: 1 })— test business logic độc lập với DB. - Middleware testing: gọi middleware với mock
req,res,nextobjects — verifynext()được gọi hayres.status()được set. - Integration tests: dùng test DB riêng (
TEST_DATABASE_URL), chạy migrations trước test suite, truncate tables giữa tests (afterEach). - Mock strategies:
jest.mock()cho modules,jest.spyOn()cho methods,jest.useFakeTimers()cho time-dependent code. - Test database setup:
globalSetupchạy migration một lần,beforeEachtruncate data,afterAlldisconnect. - Coverage:
jest --coverage+coverageThreshold: { global: { lines: 80 } }enforce minimum. - Lưu ý: không isolate tests → test order dependent failures; mock tất cả external calls (HTTP, email) để tests deterministic.
- Test pyramid for a Node.js API: unit tests (service/utils logic) → integration tests (controller + real DB) → E2E tests (full HTTP).
- Controller testing with supertest:
const res = await request(app).post('/users').send({ email: 'test@test.com' }); expect(res.status).toBe(201)— no need to start a real server; supertest injects requests directly. - Service testing: mock the DB layer with
jest.mock('../db')→(db.findUser as jest.Mock).mockResolvedValue({ id: 1 })— test business logic independently of the DB. - Middleware testing: call middleware with mock
req,res, andnextobjects — verifynext()was called orres.status()was set correctly. - Integration tests: use a separate test DB (
TEST_DATABASE_URL), run migrations before the test suite, and truncate tables between tests (afterEach). - Mock strategies:
jest.mock()for modules,jest.spyOn()for methods,jest.useFakeTimers()for time-dependent code. - Test database setup:
globalSetupruns migrations once,beforeEachtruncates data,afterAlldisconnects. - Coverage:
jest --coverage+coverageThreshold: { global: { lines: 80 } }enforces a minimum.
Pitfall: not isolating tests → test-order-dependent failures; mock all external calls (HTTP, email) to keep tests deterministic.