Kiểm thử (Tests): mạng lưới an toàn của bạn
Một bài kiểm thử (test) là đoạn mã kiểm tra xem đoạn mã khác có làm đúng như bạn mong đợi hay không. Sau khi được viết ra, nó chạy trong vài giây và cho bạn biết ngay lập tức nếu có gì đó bị thụt lùi (regression).
Một bộ kiểm thử lành mạnh trông giống như một kim tự tháp: rất nhiều bài kiểm thử đơn vị (unit test) nhỏ và nhanh ở đáy, ít bài kiểm thử tích hợp (integration test) hơn ở giữa, và chỉ một số ít bài kiểm thử đầu-cuối (end-to-end) chậm ở đỉnh. Phần lớn sự an toàn của bạn đến một cách rẻ tiền từ phía đáy:
╱╲
╱ ╲ E2E few · slow · whole app
╱────╲ (end-to-end)
╱ ╲
╱ INTEG ╲ INTEGRATION some · medium
╱──────────╲ (parts together)
╱ ╲
╱ UNIT ╲ UNIT many · fast · one function
╱────────────────╲
cheap base = most of your tests
Bạn không cần độ phủ (coverage) 100%. Hãy bắt đầu bằng việc kiểm thử những thứ sẽ gây tổn hại nhiều nhất nếu chúng hỏng: đăng nhập, thanh toán, dữ liệu không thể tạo lại được, logic nghiệp vụ cốt lõi. AI rất giỏi viết bài kiểm thử — thậm chí còn giỏi hơn cả viết tính năng, vì hành vi mong đợi ở đây rất cụ thể.
Đây là một câu lệnh () hoạt động tốt:
Write tests for the calculateDiscount function in src/pricing.js.
Cover: a normal case, the zero/empty case, the maximum-discount
boundary, and one invalid input. Use the existing test setup in
this repo (check how other *.test.js files are structured).
Run the tests and show me they pass before you finish.
Câu cuối cùng đó rất quan trọng. Luôn yêu cầu AI chạy các bài kiểm thử, chứ không chỉ viết ra chúng. Một bài kiểm thử chưa bao giờ được thực thi chỉ là một lời phỏng đoán. Cũng đáng để nói cụ thể về những trường hợp nào cần kiểm thử, vì nếu để mặc, AI có xu hướng viết ba biến thể của đường đi thuận rồi coi đó là đầy đủ. Những trường hợp thực sự bắt được lỗi lại là những trường hợp nhàm chán: đầu vào rỗng, giá trị biên, thứ lẽ ra phải gây lỗi (throw).
Một ví dụ đơn giản về những gì nó nên tạo ra:
import { calculateDiscount } from "./pricing.js";
import { describe, it, expect } from "vitest";
describe("calculateDiscount", () => {
it("applies a normal percentage discount", () => {
expect(calculateDiscount(100, 0.2)).toBe(80);
});
it("returns the full price when discount is zero", () => {
expect(calculateDiscount(100, 0)).toBe(100);
});
it("never discounts below zero", () => {
expect(calculateDiscount(100, 1.5)).toBe(0);
});
it("rejects a negative price", () => {
expect(() => calculateDiscount(-10, 0.2)).toThrow();
});
});
Khi bạn thay đổi một tính năng sau này, hãy chạy các bài kiểm thử trước. Nếu chúng chuyển sang màu đỏ (fail), bạn đã tìm thấy chỗ hỏng trước khi người dùng của bạn phát hiện ra. Khi AI sửa một lỗi, hãy yêu cầu nó thêm một bài kiểm thử sẽ thất bại với hành vi cũ — như vậy lỗi đó không thể lặng lẽ quay trở lại. Thói quen này có một cái tên: bài kiểm thử hồi quy (regression test). Mỗi lỗi bạn gặp phải là một bài học mà codebase đã phải trả giá; một bài kiểm thử hồi quy là cách bạn giữ lại bài học đó.
Một lời cảnh báo: một bài kiểm thử cũng có thể sai. Nếu AI viết một bài kiểm thử khẳng định rằng hành vi có lỗi là đúng, nó sẽ luôn pass trong khi ứng dụng của bạn vẫn bị hỏng. Vì vậy hãy đọc các khẳng định (assertion), chứ không chỉ nhìn dấu tích xanh. Một bài kiểm thử pass chỉ có nghĩa là "đoạn mã làm đúng như bài kiểm thử nói" — bạn vẫn phải xác nhận rằng bài kiểm thử nói đúng điều cần nói.