Ba tầng test
| Loại | Phạm vi | Tốc độ | Ví dụ |
|---|---|---|---|
| Unit test | Một hàm/class, cô lập dependency | Rất nhanh | Test hàm tính thuế, validate email |
| Integration test | Nhiều thành phần với nhau | Vừa | Controller + database thật |
| E2E test | Cả luồng người dùng qua UI | Chậm | Đăng ký → đăng nhập → tạo job |
Quy tắc kim tự tháp: nhiều unit test (nhanh, rẻ), vừa integration, ít E2E. Nếu bạn chỉ có E2E, bộ test của bạn vừa chậm vừa giòn.
Một unit test tốt có gì
// File: calc.test.js
import { describe, it, expect } from 'vitest';
import { applyDiscount } from './calc.js';
describe("applyDiscount", () => {
it("giảm 10% cho voucher sale10", () => {
expect(applyDiscount(100000, "sale10")).toBe(90000);
});
it("giữ nguyên giá khi không có voucher", () => {
expect(applyDiscount(50000, "none")).toBe(50000);
});
});
💡 AAA: Arrange – Act – Assert
Mỗi test nên có 3 phần rõ ràng: chuẩn bị dữ liệu (Arrange), gọi hàm (Act), kiểm tra kết quả (Assert). Test đọc như một câu chuyện ngắn — người khác hiểu ngay ý định.
Mock: cô lập để test nhanh
Unit test không gọi DB hay API thật. Dùng mock/fake thay thế — nhờ Dependency Injection ở bài SOLID mà bạn truyền fake vào dễ dàng.
// File: mock.test.js
// UserRepo thật truy cập DB, nhưng test chỉ cần object giả
const fakeUserRepo = {
async create({ email }) { return { id: 1, email }; },
async findByEmail(email) { return null; },
};
const register = makeRegister(fakeUserRepo, fakeCache, fakeMailer);
const user = await register("a@b.c", "123456");
expect(user.email).toBe("a@b.c");
TDD: đỏ – xanh – refactor
- Viết test thất bại (đỏ). 2) Viết code tối thiểu để qua (xanh). 3) Refactor cho sạch mà test vẫn xanh. Vòng lặp nhỏ này ép bạn thiết kế API dùng được ngay từ đầu.
// File: tdd.js
// 1. ĐỎ — viết test trước
it("trả về 0 khi danh sách rỗng", () => {
expect(totalPrice([])).toBe(0);
});
// 2. XANH — code tối thiểu để test qua
export const totalPrice = (items) =>
items.reduce((sum, it) => sum + it.price * it.qty, 0);
// 3. REFACTOR — sạch hơn, test vẫn xanh
❓ Unit test gọi database thật trong test case của nó. Điều gì bị vi phạm?
- Phân biệt unit / integration / E2E
- Viết test theo AAA
- Dùng mock để cô lập dependency
- Biết chu kỳ đỏ–xanh–refactor của TDD