| name | backend-testing |
| version | 1.0.0 |
| description | Backend testing guide — unit, integration, API contract, database, load testing |
| requires | [] |
| task_types | ["testing","backend-testing","qa","api-testing"] |
| applies_to_tags | ["testing","backend","qa","api","database"] |
Backend Testing Skill
Thứ tự ưu tiên test
- Integration tests — API endpoint với real database (highest signal)
- Unit tests — service logic, utilities, transformers
- Contract tests — verify API shape khớp với client expectation
- Load tests — endpoints chịu tải production estimate
Integration Test (real database)
Nguyên tắc
- Test với real database, không mock — mock database tạo false confidence
- Mỗi test tự seed data → chạy → cleanup
- Dùng transaction rollback để isolate test cases
beforeEach(async () => {
await db.delete(tasks)
testTask = await db.insert(tasks).values(mockTask).returning()
})
test('PATCH /tasks/:id updates status', async () => {
const res = await request(app)
.patch(`/api/vault/tasks/${testTask.id}`)
.send({ status: 'done' })
expect(res.status).toBe(200)
expect(res.body.data.status).toBe('done')
const row = await db.query.tasks.findFirst({ where: eq(tasks.id, testTask.id) })
expect(row.status).toBe('done')
})
Checklist endpoint test
Unit Test
Cái gì nên unit test
- Pure transformation functions
- Validation logic
- Business rules phức tạp (không liên quan DB)
- Error mapping
Cái gì KHÔNG unit test riêng
- Route handler (test via integration)
- Database queries (test via integration)
- External service calls (mock + integration riêng)
Contract Test
Đặc biệt quan trọng với RelayHQ — server và web phải khớp:
test('GET /api/vault/read-model returns expected shape', async () => {
const res = await request(app).get('/api/vault/read-model')
expect(res.body).toMatchObject({
tasks: expect.arrayContaining([
expect.objectContaining({
id: expect.any(String),
status: expect.any(String),
title: expect.any(String),
})
])
})
})
Chạy contract tests sau mỗi thay đổi API response shape.
Database Test
- Migration up/down phải idempotent
- Test constraint violations (unique, not null, foreign key)
- Test index effectiveness với
EXPLAIN ANALYZE
- Seed scripts phải nhất quán và reproducible
Load / Performance Test (k6 / autocannon)
export default function () {
const res = http.get('http://localhost:44210/api/vault/read-model')
check(res, { 'status 200': (r) => r.status === 200 })
sleep(1)
}
Chạy load test trước khi ship feature có query mới.
Mocking External Services
vi.mock('../services/external-llm', () => ({
generateSummary: vi.fn().mockResolvedValue('mocked summary')
}))
- External APIs: mock ở service boundary
- File system: dùng temp directory thật
- Time: mock
Date.now() cho time-dependent logic
Coverage targets
| Layer | Minimum |
|---|
| Service / business logic | 90% |
| Route handlers | 85% (via integration) |
| Utilities / helpers | 95% |
| Migration files | manual verification |
Khi test fail ở CI
- Chạy locally với same env vars
- Kiểm tra database state (dirty data từ test trước?)
- Check race condition nếu tests chạy parallel
- Đọc full error + stack trace — đừng skip
- Không sửa assertion để match broken behavior