DDevArchive
Đăng nhập

Integration testing: test API endpoint từ đầu đến cuối

Unit test kiểm tra từng hàm riêng lẻ. Nhưng khi ghép lại, vẫn có thể hỏng: request gửi sai format, middleware chặn nhầm, database trả khác mong đợi. Integration test gọi API thật và kiểm tra response.

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