Guides unit, integration, and component testing for Node.js/Express backend services. Use when writing, fixing, or running unit tests, integration tests, or component tests for the write-service or similar backend APIs.
Guidance for unit, integration, and component tests in the write-service (Jest + TypeScript).
| Type | Scope | Mocks | Run Command |
|---|---|---|---|
| Unit | Single module (service, controller, model) | Yes – external deps | npm run test:unit |
| Integration | API routes + real DB (no full container stack) | No – real DB | Not yet configured |
| Component | Full HTTP API + real DB + containers | No – live service | npm run test:component |
test/unit/**/*.test.tsjest.config.unit.jsnpm run test:unit1. Mock dependencies at module level
jest.mock('../../../src/models/exerciseModel');
jest.mock('../../../src/models/workoutDayModel');
const mockExerciseModel = ExerciseModel as jest.Mocked<typeof ExerciseModel>;
const mockWorkoutDayModel = WorkoutDayModel as jest.Mocked<typeof WorkoutDayModel>;
2. Controllers – mock req/res
req = { params: {}, body: {} };
res = { status: jest.fn().mockReturnThis(), json: jest.fn().mockReturnThis() };
3. Services – mock models, assert calls
mockWorkoutDayModel.exists.mockResolvedValue(true);
mockExerciseModel.create.mockResolvedValue(created);
const result = await ExerciseService.create(input);
expect(mockWorkoutDayModel.exists).toHaveBeenCalledWith(1);
expect(mockExerciseModel.create).toHaveBeenCalledWith(input);
expect(result).toEqual(created);
4. Cleanup
beforeEach(() => jest.clearAllMocks());
afterEach(() => consoleErrorSpy?.mockRestore());
src/index.ts, src/config/database.tscoverage/ (lcov)Test API routes with a real database, no mocks. Exercise request validation, DB queries, and error handling without running the full container stack.
jest.config.integration.js with testMatch: ['**/test/integration/**/*.test.ts']xq_fitness with test credentials) or Docker Composesupertest against app, run migrations, seed if neededimport request from 'supertest';
import app from '../../src/app';
describe('POST /api/v1/routines', () => {
it('creates routine and persists to DB', async () => {
const res = await request(app)
.post('/api/v1/routines')
.send({ name: 'Test', isActive: true })
.expect(201);
expect(res.body.id).toBeDefined();
// Optionally query DB to verify
});
});
Component tests hit the live API over HTTP with a real database. Follow the project workflow.
Use the component-test rule: .cursor/rules/component-test.mdc (or write-service/.cursor/rules/component-test.mdc).
./build-write-service.sh (from write-service root)npm run generate:client then npm installxq-infra generate -f ./test-env and xq-infra upnpm run test:component or npm run test:component:cixq-infra downtest/component/workflows/**/*.test.tstest/component/helpers/ (api-client, test-data, cleanup, database)test/component/setup.ts, teardown.tsjest.config.component.js (uses @chauhaidang/xq-test-utils)ApiClient from helpers/api-client.ts (generated client wrapper)testData from helpers/test-data.ts for unique payloadsCleanupHelper to track and delete created resourcesDatabaseHelper from @chauhaidang/xq-test-utils for DB assertionsAPI_BASE_URL, HEALTH_CHECK_URL (test-env gateway on 8080)If startup fails, check for other stacks (e.g. read-service) using the same ports. Run xq-infra down in other service directories.
Unit tests
jest.clearAllMocks() in beforeEachComponent tests
.cursor/rules/component-test.mdcxq-infra up before testsCleanupHelper and unique test dataIntegration tests (if added)
supertest against the Express app