DDevArchive
Đăng nhập

Unit testing, integration test và TDD

Test không phải để "phủ lệnh". Test là tấm lưới bắt hồi quy: sửa code không sợ đập vỡ thứ khác. TDD đảo ngược thứ tự — viết test trước, rồi viết code cho test xanh.

Ba tầng test

LoạiPhạm viTốc độVí dụ
Unit testMột hàm/class, cô lập dependencyRất nhanhTest hàm tính thuế, validate email
Integration testNhiều thành phần với nhauVừaController + database thật
E2E testCả luồng người dùng qua UIChậ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

  1. 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