Integration test vs Unit test
Unit test dùng mock/fake. Integration test gọi thật: qua middleware, qua controller, xuống database (test DB riêng), và kiểm tra response HTTP. Chậm hơn nhưng bắt được bug “ghép nối”.
Test API với supertest
// File: users.test.js
import { describe, it, expect, beforeAll, afterAll } from 'vitest'
import request from 'supertest'
import { app } from '../app'
import { db } from '../db'
describe('POST /api/users', () => {
beforeAll(async () => {
await db.migrate.latest() // tạo bảng
await db.seed.run() // seed dữ liệu test
})
afterAll(async () => {
await db.destroy() // đóng kết nối
})
it('tạo user mới thành công', async () => {
const res = await request(app)
.post('/api/users')
.send({ email: 'test@example.com', password: 'Abc12345!' })
expect(res.status).toBe(201)
expect(res.body.user).toHaveProperty("id")
expect(res.body.user.email).toBe("test@example.com")
// Mật khẩu KHÔNG được trả về trong response
expect(res.body.user).not.toHaveProperty("passwordHash")
})
it('từ chối email trùng', async () => {
const res = await request(app)
.post('/api/users')
.send({ email: 'test@example.com', password: 'Xyz98765!' })
expect(res.status).toBe(409)
})
it('từ chối password yếu', async () => {
const res = await request(app)
.post('/api/users')
.send({ email: 'new@example.com', password: '123' })
expect(res.status).toBe(400)
expect(res.body.error).toContain("password")
})
})
💡 Dùng database riêng cho test
KHÔNG test trên database development/production. Tạo database test riêng (hoặc SQLite in-memory), seed dữ liệu trước mỗi suite, dọn sạch sau. Đảm bảo test chạy isolated.
Test authentication flow
// File: auth.test.js
it('trả 401 khi không có token', async () => {
const res = await request(app)
.get('/api/profile')
expect(res.status).toBe(401)
})
it('trả profile khi có token hợp lệ', async () => {
// Đăng nhập lấy token trước
const login = await request(app)
.post('/api/auth/login')
.send({ email: 'test@example.com', password: 'Abc12345!' })
const res = await request(app)
.get('/api/profile')
.set("Authorization", `Bearer ${login.body.accessToken}`)
expect(res.status).toBe(200)
expect(res.body.email).toBe("test@example.com")
})
❓ Integration test khác unit test ở điểm nào?
- Dùng supertest gọi API endpoint
- Setup/teardown database cho test
- Test happy path + error case
- Test authentication flow