Một ca kiểm thử chỉ có giá trị khi nó đỏ đúng lúc mã sai. Theo thước đo đó, một phần không nhỏ kiểm thử do AI sinh ra không đáng giữ: chúng xanh khi mã đúng, xanh cả khi mã sai, và đỏ khi ai đó sắp xếp lại mã mà không đổi hành vi. Chúng làm bộ kiểm thử chậm, làm người ta mệt, và tệ nhất là cho cảm giác an toàn không có thật.
Bài này gọi tên bảy kiểu kiểm thử như vậy, mỗi kiểu kèm cách nhận ra khi đọc và cách viết lại, rồi đưa ra bảng quyết định loại mã nào đáng kiểm thử, và quy trình dọn một bộ kiểm thử đã phình to. Ví dụ là ví dụ giả định, chủ yếu bằng JavaScript với Jest.
Thước đo của một ca kiểm thử đáng giữ
Trước khi xét từng kiểu, cần thống nhất thế nào là kiểm thử tốt. Có ba câu hỏi. Một, nếu ai đó vô tình làm sai hành vi mà ca này kiểm, nó có đỏ không. Hai, nếu ai đó sắp xếp lại mã bên trong mà hành vi bên ngoài không đổi, nó có giữ xanh không. Ba, khi nó đỏ, người đọc có hiểu ngay vì sao không.
Kiểm thử AI viết hay trượt câu một và câu hai theo hai hướng ngược nhau. Hoặc nó quá lỏng, không bao giờ đỏ. Hoặc nó dính chặt vào cách cài đặt, đỏ mỗi khi mã đổi hình. Câu ba thì trượt khi tên ca và thông điệp lỗi chung chung, kiểu kiểm thử tên là kiểm tra hàm hoạt động, đỏ lên thì chỉ báo hai giá trị không bằng nhau mà không nói đang kiểm quy tắc nào.
Giữ ba câu này trong đầu khi đọc kiểm thử, bạn sẽ thấy phần lớn bảy kiểu dưới đây chỉ là những cách khác nhau để trượt chúng. Và bạn cũng có thể đưa đúng ba câu này vào câu lệnh khi nhờ AI viết hay rà kiểm thử: AI áp dụng tiêu chí rõ ràng tốt hơn nhiều so với yêu cầu chung chung kiểu "viết kiểm thử chất lượng".
Vì sao AI hay sinh ra kiểm thử kiểu này
Hiểu nguyên nhân giúp bạn chặn từ câu lệnh thay vì dọn sau. Nguyên nhân đầu tiên là thông tin đầu vào. Khi bạn chỉ đưa mã và nói "viết kiểm thử", nguồn sự thật duy nhất AI có là chính đoạn mã đó. Nó không biết quy tắc nghiệp vụ đúng là gì, nên cách hợp lý nhất với nó là mô tả lại hành vi đang thấy. Kiểm thử lặp công thức và kiểm thử chụp hành vi hiện tại là hệ quả tự nhiên.
Nguyên nhân thứ hai là mục tiêu ngầm. Nếu yêu cầu là "kiểm thử phải chạy qua" hoặc "tăng độ phủ", AI tối ưu đúng điều đó. Câu kiểm lỏng và đồ giả trả sẵn kết quả là cách chắc ăn nhất để qua. Tác tử AI chạy trong vòng lặp sửa cho tới khi xanh còn có thêm động lực đổi giá trị mong đợi khi bế tắc.
Nguyên nhân thứ ba là mẫu phổ biến. Rất nhiều mã kiểm thử công khai dùng ảnh chụp lớn, giả lập dày đặc, lệnh chờ cố định, vì người viết cũng vội. AI học theo những gì nó thấy nhiều. Cách khắc phục cho cả ba nguyên nhân là như nhau: đưa đặc tả và ví dụ do người tính, nói rõ tiêu chí ca tốt, và cấm sửa kiểm thử cũ khi đang sửa mã.
Kiểu 1 đến 3: kiểm thử không bao giờ đỏ
Lặp lại công thức của mã. Giá trị mong đợi được tính bằng chính hằng số hoặc hàm phụ mà mã dùng. Nếu hằng số sai, cả hai cùng sai. Dấu hiệu: bên phải dấu so sánh có phép tính hoặc tên nhập từ chính tệp mã đang kiểm. Viết lại bằng con số cụ thể do người tính từ đặc tả.
Câu kiểm luôn đúng. Kiểm "đã được định nghĩa", "không rỗng", "là một đối tượng", "hàm không ném lỗi". Có ích ở vài chỗ hiếm hoi, còn lại gần như mọi kết quả sai vẫn qua. Dấu hiệu: không thấy giá trị cụ thể nào trong ca kiểm. Viết lại bằng so sánh với kết quả đầy đủ hoặc với những trường quan trọng.
Giả lập toàn bộ. Mọi phụ thuộc bị thay bằng đồ giả trả sẵn kết quả, ca kiểm chỉ xác nhận hàm có gọi đồ giả. Logic thật nằm giữa các lời gọi không được kiểm. Dấu hiệu: số dòng dựng đồ giả nhiều hơn số dòng kiểm. Viết lại bằng cách chỉ giả phần ra ngoài ranh giới như mạng, thời gian, và dùng dữ liệu thật ở bên trong.
Ví dụ giả định: một ca lặp công thức và bản viết lại
Giả sử mô-đun tính thuế có hằng số tỷ lệ và hàm tính. AI đọc mã rồi viết:
const { tinhThue, TY_LE_THUE } = require('./thue');
test('tính thuế đúng', () => {
expect(tinhThue(200000)).toBe(200000 * TY_LE_THUE);
});
Ca này không đỏ khi ai đó gõ nhầm tỷ lệ, vì nó đọc đúng cái tỷ lệ đang nhầm. Nó chỉ kiểm rằng hàm nhân hai số, điều mà chính mã đã nói rõ. Bản viết lại dùng con số tính tay từ đặc tả (tỷ lệ trong ví dụ là giả định, không phải mức thuế thật):
test('thuế 10% trên giá trước thuế, làm tròn tới đồng', () => {
expect(tinhThue(200000)).toBe(20000);
expect(tinhThue(199999)).toBe(20000);
});
Dòng thứ hai còn ghi lại một quyết định nghiệp vụ là làm tròn tới đồng: 10% của 199.999 là 19.999,9, làm tròn thành 20.000. Nếu ai đó đổi sang cắt bỏ phần lẻ, ca này đỏ, và tên ca nói ngay vì sao.
Để ý rằng bản viết lại không còn nhập hằng số tỷ lệ từ mã. Kiểm thử đứng ngoài mã và nhìn vào bằng con mắt của đặc tả, đúng như một người kiểm tra độc lập. Khi nhờ AI sửa các ca kiểu này hàng loạt, bạn có thể dặn thẳng: không được dùng hằng số hay hàm nào của mô-đun đang kiểm ở vế giá trị mong đợi, mọi giá trị mong đợi phải là giá trị cụ thể, và liệt kê những ca bạn chưa có đủ thông tin để tính để người quyết.
Kiểu 4 đến 7: kiểm thử đỏ sai lúc hoặc xanh nhờ gian lận
Ảnh chụp kết quả cập nhật mù. Kiểm thử ảnh chụp (snapshot) lưu toàn bộ đầu ra rồi so ở lần sau. Khi đầu ra đổi, người ta, hoặc tác tử AI, chạy lệnh cập nhật ảnh chụp mà không đọc phần khác biệt. Ảnh chụp lớn hàng trăm dòng càng dễ bị cập nhật mù. Giữ ảnh chụp nhỏ, cho đúng phần quan trọng, và coi mọi thay đổi ảnh chụp là phần phải rà.
Kiểm chi tiết cài đặt. Kiểm thứ tự gọi các hàm nội bộ, kiểm giá trị của biến riêng tư, kiểm số lần một hàm phụ được gọi. Sắp xếp lại mã là đỏ hàng loạt dù hành vi không đổi. Kiểm thử loại này phạt chính việc dọn mã mà nó đáng ra phải bảo vệ.
Phụ thuộc thời gian và thứ tự. Dùng giờ hệ thống thật, chờ cố định vài trăm mili giây, dựa vào dữ liệu ca trước để lại. Kết quả là kiểm thử chập chờn. Cố định thời gian bằng đồng hồ giả, chờ theo điều kiện, mỗi ca tự dựng và tự dọn dữ liệu của mình.
Được sửa cho xanh. Ca đỏ thì giá trị mong đợi bị đổi theo kết quả mới, hoặc bị đánh dấu bỏ qua, hoặc bị bọc trong khối bắt lỗi. Đây là kiểu nguy hiểm nhất vì nó xoá bằng chứng. Mọi thay đổi ở tệp kiểm thử đi kèm thay đổi mã cần được đọc với câu hỏi: ca này được sửa vì đặc tả đổi hay vì mã đổi.
Loại mã nào đáng kiểm thử, loại nào không
Không phải dòng mã nào cũng cần kiểm thử riêng. AI sẵn sàng viết kiểm thử cho mọi thứ, nên quyết định phải đến từ bạn:
| Loại mã | Có nên viết kiểm thử riêng | Ghi chú |
|---|---|---|
| Quy tắc nghiệp vụ, tính tiền, phân quyền | Có, kỹ | Đây là nơi lỗi đắt nhất |
| Xử lý dữ liệu, chuyển đổi định dạng | Có | Nhiều ca mép, kiểm thử theo thuộc tính hợp ở đây |
| Hàm chỉ gán và trả giá trị, không có logic | Không | Kiểm gián tiếp qua kiểm thử của nơi dùng nó |
| Mã do công cụ sinh tự động từ đặc tả | Thường không | Kiểm đặc tả và công cụ sinh, không kiểm sản phẩm sinh ra |
| Mã nối dây đơn thuần giữa các thành phần | Ít | Kiểm thử tích hợp vài luồng chính hiệu quả hơn |
| Kịch bản dùng một lần, mã sắp bỏ | Không | Chạy thử và đọc kỹ là đủ |
Khi giao việc cho AI, bạn có thể đưa luôn bảng này vào câu lệnh: chỉ viết kiểm thử cho logic nghiệp vụ và xử lý dữ liệu, bỏ qua hàm không có logic. Số kiểm thử ít đi nhưng mỗi ca có lý do tồn tại.
Dọn một bộ kiểm thử đã phình to
Nếu dự án đã tích luỹ nhiều kiểm thử do AI viết mà không ai lọc, dọn từng phần thay vì xoá ồ ạt:
- Chọn một mô-đun quan trọng, liệt kê các ca kiểm thử của nó. Có thể nhờ AI phân loại từng ca theo bảy kiểu trên, kèm lý do; bạn đọc lại phân loại.
- Với mỗi ca bị nghi vô nghĩa, thử gài một lỗi nhỏ vào phần mã nó được cho là kiểm. Nếu ca vẫn xanh, đó là bằng chứng.
- Viết lại những ca có ý đúng mà cách kiểm sai, như lặp công thức hay câu kiểm lỏng. Xoá những ca trùng hoặc chỉ kiểm chi tiết cài đặt.
- Chạy lại toàn bộ, ghi thời gian chạy trước và sau.
- Gộp thành một thay đổi riêng, mô tả rõ đã xoá gì và vì sao, để người rà kiểm được lập luận.
- Thêm vào tệp hướng dẫn cho AI những kiểu kiểm thử nhóm không chấp nhận, để lần sau không sinh lại.
Dọn kiểm thử cũng là việc có rủi ro: xoá nhầm một ca đang bảo vệ hành vi quan trọng thì không ai biết cho tới khi lỗi lọt. Vì vậy bước gài lỗi là bước không bỏ được, và thay đổi xoá kiểm thử nên có người thứ hai duyệt.
Câu hỏi thường gặp
Kiểm thử ảnh chụp có nên bỏ hẳn không?
Không cần bỏ hẳn. Ảnh chụp nhỏ cho đầu ra ổn định như cấu trúc dữ liệu trả về hay đoạn giao diện tĩnh vẫn có ích. Vấn đề là ảnh chụp quá lớn và thói quen cập nhật mà không đọc phần khác biệt. Giữ nhỏ và rà mọi lần cập nhật.
Làm sao biết kiểm thử của mình có lặp công thức không?
Nhìn vế giá trị mong đợi. Nếu nó là một phép tính dùng hằng số hoặc hàm nhập từ chính mã đang kiểm, gần như chắc là lặp. Giá trị mong đợi tốt là con số hoặc dữ liệu cụ thể mà người tính được từ đặc tả, không cần đọc mã.
Giả lập bao nhiêu là quá nhiều?
Không có con số cố định, nhưng dấu hiệu rõ là khi phần dựng đồ giả dài hơn phần kiểm, hoặc khi đồ giả thay cả logic của chính đơn vị đang kiểm. Giả những gì ra ngoài ranh giới như mạng, thời gian, dịch vụ ngoài; giữ thật phần tính toán bên trong.
Có nên cấm AI viết kiểm thử không?
Không cần. AI viết kiểm thử nhanh và đôi khi nghĩ ra ca mép mà bạn bỏ sót. Điều cần là tiêu chí rõ ràng trong câu lệnh, đặc tả do người cung cấp, và mỗi lô kiểm thử AI viết đều được đọc và gài lỗi thử trước khi giữ lại.
Xoá kiểm thử có làm giảm độ phủ không?
Có thể giảm một chút, và điều đó không sao nếu ca bị xoá vốn không kiểm gì. Độ phủ đo dòng mã được chạy qua, không đo dòng mã được kiểm. Một bộ kiểm thử ít hơn mà mỗi ca đều đỏ đúng lúc có giá trị hơn.
Kiểm thử chập chờn có phải do AI viết không?
Không riêng gì AI, nhưng AI hay dùng giờ hệ thống thật và lệnh chờ cố định vì đó là mẫu phổ biến. Khi đọc kiểm thử AI viết, tìm các chỗ dùng thời gian, ngẫu nhiên và dữ liệu dùng chung, thay bằng đồng hồ giả, seed cố định và dữ liệu riêng cho từng ca.